diff options
36 files changed, 3249 insertions, 460 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b26e6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cache/*.php +logs/* diff --git a/bin/disable-all-console-messages b/bin/disable-all-console-messages new file mode 100755 index 0000000..4af5f3b --- /dev/null +++ b/bin/disable-all-console-messages @@ -0,0 +1,4 @@ +#!/bin/bash + +sed -r -i -e 's|^(\s*)console|\1//console|g' `find . -type f -name "*.php"` + diff --git a/bin/enable-all-console-messages b/bin/enable-all-console-messages new file mode 100755 index 0000000..d5640d5 --- /dev/null +++ b/bin/enable-all-console-messages @@ -0,0 +1,4 @@ +#!/bin/bash + +sed -r -i -e 's|^(\s*)//console|\1console|g' `find . -type f -name "*.php"` + diff --git a/doc/kolab_wap-3.0.0.sql b/doc/kolab_wap-3.0.0.sql index b94eb00..641cadd 100644 --- a/doc/kolab_wap-3.0.0.sql +++ b/doc/kolab_wap-3.0.0.sql @@ -1,11 +1,11 @@ -- phpMyAdmin SQL Dump --- version 3.4.8 +-- version 3.5.1 -- http://www.phpmyadmin.net -- -- Host: localhost --- Generation Time: Mar 29, 2012 at 04:24 PM --- Server version: 5.5.13 --- PHP Version: 5.3.8 +-- Generation Time: May 25, 2012 at 12:21 PM +-- Server version: 5.5.23 +-- PHP Version: 5.4.1 SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; @@ -17,7 +17,7 @@ SET time_zone = "+00:00"; /*!40101 SET NAMES utf8 */; -- --- Database: `kolab_wap` +-- Database: `kolab` -- -- -------------------------------------------------------- @@ -34,15 +34,16 @@ CREATE TABLE IF NOT EXISTS `group_types` ( `attributes` longtext NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ; +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ; -- -- Dumping data for table `group_types` -- INSERT INTO `group_types` (`id`, `key`, `name`, `description`, `attributes`) VALUES -(1, 'kolab', 'Kolab Distribution Group', 'A Kolab Distribution Group (with mail address)', 's:187:"{"auto_form_fields":{"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames"]},"form_fields":{"cn":[],"uniquemember":{"type":"multiselect"}}}";'), -(2, 'posix', 'POSIX Group', 'A UNIX POSIX Group', 's:166:"{"auto_form_fields":{"gidnumber":[]},"fields":{"objectclass":["top","groupofuniquenames","posixgroup"]},"form_fields":{"cn":[],"uniquemember":{"type":"multiselect"}}}";'); +(1, 'kolab', 'Kolab Distribution Group', 'A Kolab Distribution Group (with mail address)', '{"auto_form_fields":{"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames"]},"form_fields":{"cn":[],"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}'), +(2, 'posix', '(Pure) POSIX Group', 'A pure UNIX POSIX Group', '{"auto_form_fields":{"gidnumber":[]},"fields":{"objectclass":["top","groupofuniquenames","posixgroup"]},"form_fields":{"cn":[],"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}'), +(3, 'posix_mail', 'Mail-enabled POSIX Group', 'A Kolab and also UNIX POSIX Group', '{"auto_form_fields":{"gidnumber":[],"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames","posixgroup"]},"form_fields":{"cn":[],"mail":{"optional":true},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}'); -- -------------------------------------------------------- @@ -60,7 +61,35 @@ CREATE TABLE IF NOT EXISTS `options` ( -- INSERT INTO `options` (`attribute`, `option_values`) VALUES -('preferredlanguage', '["aa_DJ","aa_ER","aa_ET","af_ZA","am_ET","an_ES","ar_AE","ar_BH","ar_DZ","ar_EG","ar_IN","ar_IQ","ar_JO","ar_KW","ar_LB","ar_LY","ar_MA","ar_OM","ar_QA","ar_SA","ar_SD","ar_SY","ar_TN","ar_YE","as_IN","az_AZ","be_BY","bg_BG","bn_BD","bn_IN","bokmal","br_FR","bs_BA","byn_ER","C","ca_AD","ca_ES","ca_FR","ca_IT","catalan","croatian","csb_PL","cs_CZ","cy_GB","czech","da_DK","danish","dansk","de_AT","de_BE","de_CH","de_DE","de_LU","deutsch","dutch","dz_BT","eesti","el_CY","el_GR","en_AU","en_BW","en_CA","en_DK","en_GB","en_HK","en_IE","en_IN","en_NZ","en_PH","en_SG","en_US","en_ZA","en_ZW","es_AR","es_BO","es_CL","es_CO","es_CR","es_DO","es_EC","es_ES","es_GT","es_HN","es_MX","es_NI","es_PA","es_PE","es_PR","es_PY","es_SV","estonian","es_US","es_UY","es_VE","et_EE","eu_ES","fa_IR","fi_FI","finnish","fo_FO","fr_BE","fr_CA","fr_CH","french","fr_FR","fr_LU","fy_NL","ga_IE","galego","galician","gd_GB","german","gez_ER","gez_ET","gl_ES","greek","gu_IN","gv_GB","hebrew","he_IL","hi_IN","hr_HR","hrvatski","hsb_DE","hu_HU","hungarian","hy_AM","icelandic","id_ID","is_IS","italian","it_CH","it_IT","iw_IL","ja_JP","japanese","ka_GE","kk_KZ","kl_GL","km_KH","kn_IN","ko_KR","korean","ku_TR","kw_GB","ky_KG","lg_UG","lithuanian","lo_LA","lt_LT","lv_LV","mai_IN","mg_MG","mi_NZ","mk_MK","ml_IN","mn_MN","mr_IN","ms_MY","mt_MT","nb_NO","ne_NP","nl_BE","nl_NL","nn_NO","no_NO","norwegian","nr_ZA","nso_ZA","nynorsk","oc_FR","om_ET","om_KE","or_IN","pa_IN","pa_PK","pl_PL","polish","portuguese","POSIX","pt_BR","pt_PT","romanian","ro_RO","ru_RU","russian","ru_UA","rw_RW","se_NO","sid_ET","si_LK","sk_SK","slovak","slovene","slovenian","sl_SI","so_DJ","so_ET","so_KE","so_SO","spanish","sq_AL","sr_CS","sr_ME","sr_RS","ss_ZA","st_ZA","sv_FI","sv_SE","swedish","ta_IN","te_IN","tg_TJ","thai","th_TH","ti_ER","ti_ET","tig_ER","tl_PH","tn_ZA","tr_CY","tr_TR","ts_ZA","tt_RU","turkish","uk_UA","ur_PK","uz_UZ","ve_ZA","vi_VN","wa_BE","xh_ZA","yi_US","zh_CN","zh_HK","zh_SG","zh_TW","zu_ZA"]'); +('preferredlanguage', '["aa_DJ","aa_ER","aa_ET","af_ZA","am_ET","an_ES","ar_AE","ar_BH","ar_DZ","ar_EG","ar_IN","ar_IQ","ar_JO","ar_KW","ar_LB","ar_LY","ar_MA","ar_OM","ar_QA","ar_SA","ar_SD","ar_SY","ar_TN","ar_YE","as_IN","az_AZ","be_BY","bg_BG","bn_BD","bn_IN","bokmal","br_FR","bs_BA","byn_ER","C","ca_AD","ca_ES","ca_FR","ca_IT","catalan","croatian","csb_PL","cs_CZ","cy_GB","czech","da_DK","danish","dansk","de_AT","de_BE","de_CH","de_DE","de_LU","deutsch","dutch","dz_BT","eesti","el_CY","el_GR","en_AU","en_BW","en_CA","en_DK","en_GB","en_HK","en_IE","en_IN","en_NZ","en_PH","en_SG","en_US","en_ZA","en_ZW","es_AR","es_BO","es_CL","es_CO","es_CR","es_DO","es_EC","es_ES","es_GT","es_HN","es_MX","es_NI","es_PA","es_PE","es_PR","es_PY","es_SV","estonian","es_US","es_UY","es_VE","et_EE","eu_ES","fa_IR","fi_FI","finnish","fo_FO","fr_BE","fr_CA","fr_CH","french","fr_FR","fr_LU","fy_NL","ga_IE","galego","galician","gd_GB","german","gez_ER","gez_ET","gl_ES","greek","gu_IN","gv_GB","hebrew","he_IL","hi_IN","hr_HR","hrvatski","hsb_DE","hu_HU","hungarian","hy_AM","icelandic","id_ID","is_IS","italian","it_CH","it_IT","iw_IL","ja_JP","japanese","ka_GE","kk_KZ","kl_GL","km_KH","kn_IN","ko_KR","korean","ku_TR","kw_GB","ky_KG","lg_UG","lithuanian","lo_LA","lt_LT","lv_LV","mai_IN","mg_MG","mi_NZ","mk_MK","ml_IN","mn_MN","mr_IN","ms_MY","mt_MT","nb_NO","ne_NP","nl_BE","nl_NL","nn_NO","no_NO","norwegian","nr_ZA","nso_ZA","nynorsk","oc_FR","om_ET","om_KE","or_IN","pa_IN","pa_PK","pl_PL","polish","portuguese","POSIX","pt_BR","pt_PT","romanian","ro_RO","ru_RU","russian","ru_UA","rw_RW","se_NO","sid_ET","si_LK","sk_SK","slovak","slovene","slovenian","sl_SI","so_DJ","so_ET","so_KE","so_SO","spanish","sq_AL","sr_CS","sr_ME","sr_RS","ss_ZA","st_ZA","sv_FI","sv_SE","swedish","ta_IN","te_IN","tg_TJ","thai","th_TH","ti_ER","ti_ET","tig_ER","tl_PH","tn_ZA","tr_CY","tr_TR","ts_ZA","tt_RU","turkish","uk_UA","ur_PK","uz_UZ","ve_ZA","vi_VN","wa_BE","xh_ZA","yi_US","zh_CN","zh_HK","zh_SG","zh_TW","zu_ZA"]'), +('c', '["AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","YU","ZA","ZM","ZW"]'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `resource_types` +-- + +CREATE TABLE IF NOT EXISTS `resource_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `key` text NOT NULL, + `name` varchar(256) NOT NULL, + `description` text NOT NULL, + `attributes` longtext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ; + +-- +-- Dumping data for table `resource_types` +-- + +INSERT INTO `resource_types` (`id`, `key`, `name`, `description`, `attributes`) VALUES +(1, 'collection', 'Resource Collection', 'A collection or pool of resources', '{"auto_form_fields":{"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames"]},"form_fields":{"cn":[],"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}'), +(2, 'car', 'Car', 'A car', '{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"cn":[]}}'), +(3, 'confroom', 'Conference Room', 'A conference room', '{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"cn":[]}}'), +(4, 'beamer', 'Beamer', 'A portable beamer', '{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"cn":[]}}'), +(5, 'footballtickets', 'Football Season Tickets', 'Season tickets to the game (pretty good seats too!)', '{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"cn":[]}}'); -- -------------------------------------------------------- @@ -76,15 +105,16 @@ CREATE TABLE IF NOT EXISTS `user_types` ( `attributes` longtext NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ; +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ; -- -- Dumping data for table `user_types` -- INSERT INTO `user_types` (`id`, `key`, `name`, `description`, `attributes`) VALUES -(1, 'kolab', 'Kolab User', 'A Kolab User', '{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"mail":{"data":["givenname","preferredlanguage","sn"]},"mailalternateaddress":{"data":["givenname","preferredlanguage","sn"]},"mailhost":[],"uid":{"data":["givenname","preferredlanguage","sn"]},"userpassword":[]},"form_fields":{"initials":[],"givenname":[],"mailalternateaddress":{"type":"list"},"nsrole":{"type":"list","autocomplete":true},"preferredlanguage":{"type":"select","values":["en_US","de_DE","de_CH","en_GB","fr_FR"]},"sn":[],"title":[]},"fields":{"objectclass":["top","inetorgperson","organizationalperson","person","kolabinetorgperson"]}}'), -(2, 'posix', 'POSIX User', 'A POSIX user (with a home directory and shell access)', '{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"gidnumber":[],"homedirectory":{"data":["givenname","sn"]},"uid":{"data":["givenname","sn"]},"uidnumber":[],"userpassword":[]},"form_fields":{"givenname":[],"initials":[],"preferredlanguage":{"type":"select","values_source":"<uri>"},"shell":{"type":"select","values":["/bin/bash","/usr/bin/git-shell","/sbin/nologin"]},"sn":[],"title":[]},"fields":{"objectclass":["top","inetorgperson","organizationalperson","person","posixaccount"]}}'); +(1, 'kolab', 'Kolab User', 'A Kolab User', '{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"mail":{"data":["givenname","preferredlanguage","sn"]},"alias":{"data":["givenname","preferredlanguage","sn"],"optional":true},"mailhost":{"optional":true},"uid":{"data":["givenname","preferredlanguage","sn"]},"userpassword":{"optional":true}},"form_fields":{"givenname":[],"initials":{"optional":true},"kolabdelegate":{"type":"list","autocomplete":true,"optional":true},"kolabinvitationpolicy":{"type":"select","values":["","ACT_MANUAL","ACT_REJECT"],"optional":true},"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"l":{"optional":true},"alias":{"type":"list","optional":true},"mailquota":{"optional":true},"mobile":{"optional":true},"nsroledn":{"type":"list","autocomplete":true,"optional":true},"o":{"optional":true},"ou":{"type":"select"},"pager":{"optional":true},"postalcode":{"optional":true},"preferredlanguage":{"type":"select"},"sn":[],"street":{"optional":true},"telephonenumber":{"optional":true},"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","kolabinetorgperson","mailrecipient","organizationalperson","person"]}}'), +(2, 'posix', 'POSIX User', 'A POSIX user (with a home directory and shell access)', '{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"gidnumber":[],"homedirectory":{"data":["givenname","sn"]},"uid":{"data":["givenname","sn"]},"uidnumber":[],"userpassword":{"optional":true}},"form_fields":{"givenname":[],"initials":{"optional":true},"preferredlanguage":{"type":"select","values":["en_US","de_DE","de_CH","en_GB","fi_FI","fr_FR","hu_HU"]},"loginshell":{"type":"select","values":["/bin/bash","/usr/bin/git-shell","/sbin/nologin"]},"ou":{"type":"select"},"sn":[],"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","organizationalperson","person","posixaccount"]}}'), +(3, 'kolab_posix', 'Mail-enabled POSIX User', 'A mail-enabled POSIX User', '{"auto_form_fields":{"cn":{"data":["givenname","preferredlanguage","sn"]},"displayname":{"data":["givenname","preferredlanguage","sn"]},"gidnumber":[],"homedirectory":{"data":["givenname","preferredlanguage","sn"]},"mail":{"data":["givenname","preferredlanguage","sn"]},"alias":{"data":["givenname","preferredlanguage","sn"],"optional":true},"mailhost":{"optional":true},"uid":{"data":["givenname","preferredlanguage","sn"]},"uidnumber":[],"userpassword":{"optional":true}},"form_fields":{"givenname":[],"initials":{"optional":true},"kolabdelegate":{"type":"list","autocomplete":true,"optional":true},"kolabinvitationpolicy":{"type":"select","values":["","ACT_MANUAL","ACT_REJECT"],"optional":true},"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"l":{"optional":true},"loginshell":{"type":"select","values":["/bin/bash","/usr/bin/git-shell","/sbin/nologin"]},"alias":{"type":"list","optional":true},"mailquota":{"optional":true},"mobile":{"optional":true},"nsroledn":{"type":"list","autocomplete":true,"optional":true},"o":{"optional":true},"ou":{"type":"select"},"pager":{"optional":true},"postalcode":{"optional":true},"preferredlanguage":{"type":"select"},"sn":[],"street":{"optional":true},"telephonenumber":{"optional":true},"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","kolabinetorgperson","mailrecipient","organizationalperson","person","posixaccount"]}}'); /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; diff --git a/lib/Auth.php b/lib/Auth.php index fd34e97..5176933 100644 --- a/lib/Auth.php +++ b/lib/Auth.php @@ -205,6 +205,26 @@ class Auth { return $this->_auth[$_SESSION['user']->get_domain()]->attribute_details($attributes); } + public function domain_add($domain, $parent_domain=null) + { + return $this->_auth[$_SESSION['user']->get_domain()]->domain_add($domain, $parent_domain); + } + + public function domain_edit($domain, $attributes, $typeid = null) + { + return $this->_auth[$_SESSION['user']->get_domain()]->domain_edit($domain, $attributes, $typeid); + } + + public function domain_find_by_attribute($attribute) + { + return $this->_auth[$_SESSION['user']->get_domain()]->domain_find_by_attribute($attribute); + } + + public function domain_info($domaindata) + { + return $this->_auth[$_SESSION['user']->get_domain()]->domain_info($domaindata); + } + public function find_user_groups($member_dn) { return $this->_auth[$_SESSION['user']->get_domain()]->find_user_groups($member_dn); @@ -286,6 +306,19 @@ class Auth { return $groups; } + public function list_resources($domain = NULL, $attributes = array(), $search = array(), $params = array()) + { + $this->connect($domain); + if ($domain === NULL) { + $domain = $this->conf->get('primary_domain'); + } + + $resources = $this->_auth[$domain]->list_resources($attributes, $search, $params); + + return $resources; + } + + public function list_roles($domain = NULL, $attributes = array(), $search = array(), $params = array()) { $this->connect($domain); @@ -320,6 +353,36 @@ class Auth { } } + public function resource_add($attributes, $typeid = null) + { + return $this->_auth[$_SESSION['user']->get_domain()]->resource_add($attributes, $typeid); + } + + public function resource_edit($resource, $attributes, $typeid = null) + { + return $this->_auth[$_SESSION['user']->get_domain()]->resource_edit($resource, $attributes, $typeid); + } + + public function resource_delete($subject) + { + return $this->_auth[$_SESSION['user']->get_domain()]->resource_delete($subject); + } + + public function resource_find_by_attribute($attribute) + { + return $this->_auth[$_SESSION['user']->get_domain()]->resource_find_by_attribute($attribute); + } + + public function resource_info($resourcedata) + { + return $this->_auth[$_SESSION['user']->get_domain()]->resource_info($resourcedata); + } + + public function resource_members_list($resourcedata, $recurse = true) + { + return $this->_auth[$_SESSION['user']->get_domain()]->resource_members_list($resourcedata, $recurse); + } + public function search() { $this->connect($domain); diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php index bcd3491..7146f71 100644 --- a/lib/Auth/LDAP.php +++ b/lib/Auth/LDAP.php @@ -72,7 +72,7 @@ class LDAP $domain = $_SESSION['user']->get_domain(); } catch (Exception $e) { // TODO: Debug logging - error_log("Warning, user not authenticated yet"); + //console("Warning, user not authenticated yet"); } } } @@ -128,7 +128,7 @@ class LDAP */ public function authenticate($username, $password) { - error_log("LDAP authentication request for $username"); + //console("LDAP authentication request for $username"); if (!$this->_connect()) { return false; @@ -189,17 +189,17 @@ class LDAP $_SESSION['user']->user_root_dn = $root_dn; $_SESSION['user']->user_bind_dn = $subject_dn; $_SESSION['user']->user_bind_pw = $password; - error_log("Successfully bound with User DN: " . $_SESSION['user']->user_bind_dn); + //console("Successfully bound with User DN: " . $_SESSION['user']->user_bind_dn); } else { - error_log("Successfully bound with User DN: " . $subject_dn . " but not saving it to the session"); + //console("Successfully bound with User DN: " . $subject_dn . " but not saving it to the session"); } // @TODO: return unique attribute return $subject_dn; } else { - error_log("LDAP Error: " . $this->_errstr()); + //console("LDAP Error: " . $this->_errstr()); return false; } } @@ -236,7 +236,7 @@ class LDAP } } } else { - error_log("No schema details exist for attribute $attribute (which is strange)"); + //console("No schema details exist for attribute $attribute (which is strange)"); } // The relevant parts only, please @@ -292,17 +292,71 @@ class LDAP } - public function domain_add($domain, $domain_alias = false, $prepopulate = true) + public function domain_add($domain, $parent_domain = false, $prepopulate = true) { // Apply some routines for access control to this function here. - if ($domain_alias) { - return $this->_domain_add_alias($domain, $domain_alias); + if (!empty($parent_domain)) { + return $this->_domain_add_alias($domain, $parent_domain); } else { return $this->_domain_add_new($domain, $prepopulate); } } + public function domain_edit($domain, $attributes, $typeid = null) + { + // Domain identifier + $unique_attr = $this->unique_attribute(); + $attributes[$unique_attr] = $domain; + + // Now that values have been re-generated where necessary, compare + // the new domain attributes to the original domain attributes. + $_domain = $this->domain_find_by_attribute(array($unique_attr => $attributes[$unique_attr])); + + if (!$_domain) { + //console("Could not find domain"); + return false; + } + + $_domain_dn = key($_domain); + $_domain = $this->domain_info($_domain_dn, array_keys($attributes)); + + // We should start throwing stuff over the fence here. + return $this->modify_entry($_domain_dn, $_domain[$_domain_dn], $attributes); + } + + public function domain_find_by_attribute($attribute) + { + $conf = Conf::get_instance(); + $base_dn = $conf->get('ldap', 'domain_base_dn'); + return $this->entry_find_by_attribute($attribute, $base_dn); + } + + public function domain_info($domain, $attributes = array('*')) + { + $conf = Conf::get_instance(); + + $domain_dn = $this->entry_dn($domain); + + if (!$domain_dn) { + $domain_base_dn = $conf->get('ldap', 'domain_base_dn'); + $domain_filter = $conf->get('ldap', 'domain_filter'); + $domain_name_attribute = $conf->get('ldap', 'domain_name_attribute'); + $domain_filter = "(&$domain_filter($domain_name_attribute=$domain))"; + $result = self::normalize_result($this->_search($domain_base_dn, $domain_filter, $attributes)); + } else { + $result = self::normalize_result($this->_search($domain_dn, '(objectclass=*)', $attributes)); + } + + if (!$result) { + return false; + } + + //console("domain_info() result:", $result); + + return $result; + } + public function effective_rights($subject) { $effective_rights_control_oid = "1.3.6.1.4.1.42.2.27.9.5.2"; @@ -310,7 +364,7 @@ class LDAP $supported_controls = $this->supported_controls(); if (!in_array($effective_rights_control_oid, $supported_controls)) { - error_log("No getEffectiveRights control in supportedControls"); + //console("No getEffectiveRights control in supportedControls"); return $this->legacy_rights($subject); } @@ -333,16 +387,20 @@ class LDAP //console("effective_rights for $subject resolves to $entry_dn"); + $moz_ldapsearch = "/usr/lib64/mozldap/ldapsearch"; + if (!is_file($moz_ldapsearch)) { + $moz_ldapsearch = "/usr/lib/mozldap/ldapsearch"; + } + $command = array( - // TODO: Very 64-bit specific - '/usr/lib64/mozldap/ldapsearch', + $moz_ldapsearch, '-x', '-h', $this->_ldap_server, '-p', $this->_ldap_port, '-b', - $conf->get('base_dn'), + '"' . $entry_dn . '"', '-D', '"' . $_SESSION['user']->user_bind_dn . '"', '-w', @@ -356,7 +414,9 @@ class LDAP 'dn:' . $_SESSION['user']->user_bind_dn // User DN ) ) . '"', - '"(entrydn=' . $entry_dn . ')"', + '-s', + 'base', + '"(objectclass=*)"', '"*"', ); @@ -399,6 +459,28 @@ class LDAP return $attributes; } + public function find_user_groups($member_dn) + { + //console(__FILE__ . "(" . __LINE__ . "): " . $member_dn); + + $groups = array(); + + $root_dn = $this->domain_root_dn($this->domain); + + // TODO: Do not query for both, it's either one or the other + $entries = $this->_search($root_dn, "(|" . + "(&(objectclass=groupofnames)(member=$member_dn))" . + "(&(objectclass=groupofuniquenames)(uniquemember=$member_dn))" . + ")"); + + $entries = self::normalize_result($entries); + + foreach ($entries as $entry_dn => $entry_attributes) { + $groups[] = $entry_dn; + } + + return $groups; + } public function get_attribute($subject_dn, $attribute) { @@ -422,6 +504,101 @@ class LDAP return false; } + public function group_add($attrs, $typeid = null) + { + if ($typeid == null) { + $type_str = 'group'; + } + else { + $db = SQL::get_instance(); + $_key = $db->fetch_assoc($db->query("SELECT `key` FROM group_types WHERE id = ?", $typeid)); + $type_str = $_key['key']; + } + + // Check if the group_type has a specific base DN specified. + $base_dn = $this->conf->get($type_str . "_group_base_dn"); + // If not, take the regular user_base_dn + if (!$base_dn) + $base_dn = $this->conf->get("group_base_dn"); + + // TODO: The rdn is configurable as well. + // Use [$type_str . "_"]user_rdn_attr + $dn = "cn=" . $attrs['cn'] . "," . $base_dn; + + return $this->_add($dn, $attrs); + } + + public function group_delete($group) + { + $group_dn = $this->entry_dn($group); + + if (!$group_dn) { + return false; + } + + return $this->_delete($group_dn); + } + + public function group_edit($group, $attributes, $typeid = null) + { +/* + // Get the type "key" string for the next few settings. + if ($typeid == null) { + $type_str = 'group'; + } + else { + $db = SQL::get_instance(); + $_key = $db->fetch_assoc($db->query("SELECT `key` FROM group_types WHERE id = ?", $typeid)); + $type_str = $_key['key']; + } +*/ + // Group identifier + $unique_attr = $this->unique_attribute(); + $attributes[$unique_attr] = $group; + + // Now that values have been re-generated where necessary, compare + // the new group attributes to the original group attributes. + $_group = $this->entry_find_by_attribute(array($unique_attr => $attributes[$unique_attr])); + + if (!$_group) { + //console("Could not find group"); + return false; + } + + $_group_dn = key($_group); + $_group = $this->group_info($_group_dn, array_keys($attributes)); + + // We should start throwing stuff over the fence here. + return $this->modify_entry($_group_dn, $_group[$_group_dn], $attributes); + } + + public function group_find_by_attribute($attribute) + { + return $this->entry_find_by_attribute($attribute); + } + + public function group_info($group, $attributes = array('*')) + { + $group_dn = $this->entry_dn($group); + + if (!$group_dn) { + return false; + } + + return self::normalize_result($this->_search($group_dn, '(objectclass=*)', $attributes)); + } + + public function group_members_list($group, $recurse = true) + { + $group_dn = $this->entry_dn($group); + + if (!$group_dn) { + return false; + } + + return $this->_list_group_members($group_dn, null, $recurse); + } + public function list_domains() { $domains = $this->domains_list(); @@ -484,6 +661,37 @@ class LDAP return $users; } + public function list_resources($attributes = array(), $search = array(), $params = array()) + { + if (!empty($params['sort_by'])) { + if (is_array($params['sort_by'])) { + foreach ($params['sort_by'] as $attrib) { + if (!in_array($attrib, $attributes)) { + $attributes[] = $attrib; + } + } + } else { + if (!in_array($params['sort_by'], $attributes)) { + $attributes[] = $params['sort_by']; + } + } + } + + $resources = $this->resources_list($attributes, $search); + $resources = self::normalize_result($resources); + + if (!empty($params['sort_by'])) { + $this->sort_result_key = $params['sort_by']; + uasort($resources, array($this, 'sort_result')); + + if ($params['sort_order'] == 'DESC') { + $resources = array_reverse($resources, true); + } + } + + return $resources; + } + public function list_roles($attributes = array(), $search = array(), $params = array()) { if (!empty($params['sort_by'])) { @@ -507,218 +715,207 @@ class LDAP return $roles; } - public function user_add($attrs, $typeid = null) + public function resource_add($attrs, $typeid = null) { if ($typeid == null) { - $type_str = 'user'; + $type_str = 'resource'; } else { $db = SQL::get_instance(); - $_key = $db->fetch_assoc($db->query("SELECT `key` FROM user_types WHERE id = ?", $typeid)); + $_key = $db->fetch_assoc($db->query("SELECT `key` FROM resource_types WHERE id = ?", $typeid)); $type_str = $_key['key']; } - // Check if the user_type has a specific base DN specified. - $base_dn = $this->conf->get($this->domain, $type_str . "_user_base_dn"); + // Check if the resource_type has a specific base DN specified. + $base_dn = $this->conf->get($type_str . "_resource_base_dn"); // If not, take the regular user_base_dn if (!$base_dn) - $base_dn = $this->conf->get($this->domain, "user_base_dn"); - - // If no user_base_dn either, take the user type specific from the parent - // configuration - if (!$base_dn) - $base_dn = $this->conf->get('ldap', $type_str . "_user_base_dn"); - - if (!empty($attrs['ou'])) { - $base_dn = $attrs['ou']; - } + $base_dn = $this->conf->get("resource_base_dn"); // TODO: The rdn is configurable as well. // Use [$type_str . "_"]user_rdn_attr - $dn = "uid=" . $attrs['uid'] . "," . $base_dn; + $dn = "cn=" . $attrs['cn'] . "," . $base_dn; return $this->_add($dn, $attrs); } - public function user_edit($user, $attributes, $typeid = null) + public function resource_delete($resource) + { + $resource_dn = $this->entry_dn($resource); + + if (!$resource_dn) { + return false; + } + + return $this->_delete($resource_dn); + } + + public function resource_edit($resource, $attributes, $typeid = null) { /* // Get the type "key" string for the next few settings. if ($typeid == null) { - $type_str = 'user'; + $type_str = 'resource'; } else { $db = SQL::get_instance(); - $_key = $db->fetch_assoc($db->query("SELECT `key` FROM user_types WHERE id = ?", $typeid)); + $_key = $db->fetch_assoc($db->query("SELECT `key` FROM resource_types WHERE id = ?", $typeid)); $type_str = $_key['key']; } */ + // Group identifier $unique_attr = $this->unique_attribute(); - $attributes[$unique_attr] = $user; + $attributes[$unique_attr] = $resource; // Now that values have been re-generated where necessary, compare - // the new group attributes to the original group attributes. - $_user = $this->entry_find_by_attribute(array($unique_attr => $attributes[$unique_attr])); + // the new resource attributes to the original resource attributes. + $_resource = $this->entry_find_by_attribute(array($unique_attr => $attributes[$unique_attr])); - if (!$_user) { - //console("Could not find user"); + if (!$_resource) { + //console("Could not find resource"); return false; } - $_user_dn = key($_user); - $_user = $this->user_info($_user_dn, array_keys($attributes)); - - //console("user_edit \$_user", $_user); + $_resource_dn = key($_resource); + $_resource = $this->resource_info($_resource_dn, array_keys($attributes)); // We should start throwing stuff over the fence here. - return $this->modify_entry($_user_dn, $_user[$_user_dn], $attributes); + return $this->modify_entry($_resource_dn, $_resource[$_resource_dn], $attributes); } - public function user_delete($user) + public function resource_find_by_attribute($attribute) { - $user_dn = $this->entry_dn($user); - - if (!$user_dn) { - return false; - } - - return $this->_delete($user_dn); + return $this->entry_find_by_attribute($attribute); } /** - * User attributes + * Resource attributes * * */ - public function user_info($user, $attributes = array('*')) + public function resource_info($resource, $attributes = array('*')) { - $user_dn = $this->entry_dn($user); + $resource_dn = $this->entry_dn($resource); - if (!$user_dn) + if (!$resource_dn) return false; - return self::normalize_result($this->_search($user_dn, '(objectclass=*)', $attributes)); + return self::normalize_result($this->_search($resource_dn, '(objectclass=*)', $attributes)); } - public function user_find_by_attribute($attribute) + public function resource_members_list($resource, $recurse = true) { - return $this->entry_find_by_attribute($attribute); - } + $resource_dn = $this->entry_dn($resource); - public function find_user_groups($member_dn) - { - error_log(__FILE__ . "(" . __LINE__ . "): " . $member_dn); - - $groups = array(); - - $root_dn = $this->domain_root_dn($this->domain); - - // TODO: Do not query for both, it's either one or the other - $entries = $this->_search($root_dn, "(|" . - "(&(objectclass=groupofnames)(member=$member_dn))" . - "(&(objectclass=groupofuniquenames)(uniquemember=$member_dn))" . - ")"); - - $entries = self::normalize_result($entries); - - foreach ($entries as $entry_dn => $entry_attributes) { - $groups[] = $entry_dn; + if (!$resource_dn) { + return false; } - return $groups; + return $this->_list_resource_members($resource_dn, null, $recurse); } - public function group_add($attrs, $typeid = null) + public function user_add($attrs, $typeid = null) { if ($typeid == null) { - $type_str = 'group'; + $type_str = 'user'; } else { $db = SQL::get_instance(); - $_key = $db->fetch_assoc($db->query("SELECT `key` FROM group_types WHERE id = ?", $typeid)); + $_key = $db->fetch_assoc($db->query("SELECT `key` FROM user_types WHERE id = ?", $typeid)); $type_str = $_key['key']; } - // Check if the group_type has a specific base DN specified. - $base_dn = $this->conf->get($type_str . "_group_base_dn"); + // Check if the user_type has a specific base DN specified. + $base_dn = $this->conf->get($this->domain, $type_str . "_user_base_dn"); // If not, take the regular user_base_dn - if (!$base_dn) - $base_dn = $this->conf->get("group_base_dn"); + if (empty($base_dn)) + $base_dn = $this->conf->get($this->domain, "user_base_dn"); + + // If no user_base_dn either, take the user type specific from the parent + // configuration + if (empty($base_dn)) + $base_dn = $this->conf->get('ldap', $type_str . "_user_base_dn"); + + // If still no base dn to add the user to... use the toplevel dn + if (empty($base_dn)) + $base_dn = $this->conf->get($this->domain, "base_dn"); + if (empty($base_dn)) + $base_dn = $this->conf->get('ldap', "base_dn"); + + if (!empty($attrs['ou'])) { + $base_dn = $attrs['ou']; + } + + //console("Base DN now: $base_dn"); // TODO: The rdn is configurable as well. // Use [$type_str . "_"]user_rdn_attr - $dn = "cn=" . $attrs['cn'] . "," . $base_dn; + $dn = "uid=" . $attrs['uid'] . "," . $base_dn; return $this->_add($dn, $attrs); } - public function group_edit($group, $attributes, $typeid = null) + public function user_edit($user, $attributes, $typeid = null) { /* // Get the type "key" string for the next few settings. if ($typeid == null) { - $type_str = 'group'; + $type_str = 'user'; } else { $db = SQL::get_instance(); - $_key = $db->fetch_assoc($db->query("SELECT `key` FROM group_types WHERE id = ?", $typeid)); + $_key = $db->fetch_assoc($db->query("SELECT `key` FROM user_types WHERE id = ?", $typeid)); $type_str = $_key['key']; } */ - // Group identifier $unique_attr = $this->unique_attribute(); - $attributes[$unique_attr] = $group; + $attributes[$unique_attr] = $user; // Now that values have been re-generated where necessary, compare // the new group attributes to the original group attributes. - $_group = $this->entry_find_by_attribute(array($unique_attr => $attributes[$unique_attr])); + $_user = $this->entry_find_by_attribute(array($unique_attr => $attributes[$unique_attr])); - if (!$_group) { - //console("Could not find group"); + if (!$_user) { + //console("Could not find user"); return false; } - $_group_dn = key($_group); - $_group = $this->group_info($_group_dn, array_keys($attributes)); - - // We should start throwing stuff over the fence here. - return $this->modify_entry($_group_dn, $_group[$_group_dn], $attributes); - } - - public function group_delete($group) - { - $group_dn = $this->entry_dn($group); + $_user_dn = key($_user); + $_user = $this->user_info($_user_dn, array_keys($attributes)); - if (!$group_dn) { - return false; - } + //console("Auth::LDAP::user_edit() existing \$_user info", $_user); - return $this->_delete($group_dn); + // We should start throwing stuff over the fence here. + return $this->modify_entry($_user_dn, $_user[$_user_dn], $attributes); } - public function group_info($group, $attributes = array('*')) + public function user_delete($user) { - $group_dn = $this->entry_dn($group); + $user_dn = $this->entry_dn($user); - if (!$group_dn) { + if (!$user_dn) { return false; } - return self::normalize_result($this->_search($group_dn, '(objectclass=*)', $attributes)); + return $this->_delete($user_dn); } - public function group_members_list($group, $recurse = true) + /** + * User attributes + * + * + */ + public function user_info($user, $attributes = array('*')) { - $group_dn = $this->entry_dn($group); + $user_dn = $this->entry_dn($user); - if (!$group_dn) { + if (!$user_dn) return false; - } - return $this->_list_group_members($group_dn, null, $recurse); + return self::normalize_result($this->_search($user_dn, '(objectclass=*)', $attributes)); } - public function group_find_by_attribute($attribute) + public function user_find_by_attribute($attribute) { return $this->entry_find_by_attribute($attribute); } @@ -738,11 +935,11 @@ class LDAP return false; } - error_log("Searching for domain $domain"); - error_log("From domain to root dn"); + //console("Searching for domain $domain"); + //console("From domain to root dn"); if (($this->_bind($conf->get('ldap', 'bind_dn'), $conf->get('ldap', 'bind_pw'))) == false) { - error_log("WARNING: Invalid Service bind credentials supplied"); + //console("WARNING: Invalid Service bind credentials supplied"); $this->_bind($conf->manager_bind_dn, $conf->manager_bind_pw); } @@ -770,7 +967,7 @@ class LDAP $this->_unbind(); - error_log("Using $domain_rootdn"); + //console("Using $domain_rootdn"); return $domain_rootdn; } @@ -805,16 +1002,16 @@ class LDAP return $result; } - private function _search($base_dn, $search_filter = '(objectClass=*)', $attributes = array('*')) - { - return $this->__search($base_dn, $search_filter, $attributes); - } - private function domains_list() { $section = $this->conf->get('kolab', 'auth_mechanism'); $base_dn = $this->conf->get($section, 'domain_base_dn'); - $filter = $this->conf->get($section, 'kolab_domain_filter'); + $filter = $this->conf->get($section, 'domain_filter'); + + $kolab_filter = $this->conf->get($section, 'kolab_domain_filter'); + if (empty($filter) && !empty($kolab_filter)) { + $filter = $kolab_filter; + } return $this->_search($base_dn, $filter); } @@ -837,7 +1034,7 @@ class LDAP } } - private function entry_find_by_attribute($attribute) + private function entry_find_by_attribute($attribute, $base_dn = null) { if (empty($attribute) || !is_array($attribute) || count($attribute) > 1) { return false; @@ -855,16 +1052,18 @@ class LDAP $filter .= ")"; - $base_dn = $this->domain_root_dn($this->domain); + if (empty($base_dn)) { + $base_dn = $this->domain_root_dn($this->domain); + } $result = self::normalize_result($this->_search($base_dn, $filter, array_keys($attribute))); if (count($result) > 0) { - error_log("Results found: " . implode(', ', array_keys($result))); + //console("Results found: " . implode(', ', array_keys($result))); return $result; } else { - error_log("No result"); + //console("No result"); return false; } } @@ -999,7 +1198,7 @@ class LDAP $rdn_attr = $rdn_components[0]; - //console($rdn_attr); + //console("Auth::LDAP::modify_entry() using rdn attribute: " . $rdn_attr); $mod_array = Array( "add" => Array(), // For use with ldap_mod_add() @@ -1009,32 +1208,91 @@ class LDAP ); // This is me cheating. Remove this special attribute. - $old_ou = $old_attrs['ou']; - $new_ou = $new_attrs['ou']; - unset($old_attrs['ou']); - unset($new_attrs['ou']); + if (array_key_exists('ou', $old_attrs) || array_key_exists('ou', $new_attrs)) { + $old_ou = $old_attrs['ou']; + $new_ou = $new_attrs['ou']; + unset($old_attrs['ou']); + unset($new_attrs['ou']); + } else { + $old_ou = null; + $new_ou = null; + } // Compare each attribute value of the old attrs with the corresponding value // in the new attrs, if any. foreach ($old_attrs as $attr => $old_attr_value) { if (array_key_exists($attr, $new_attrs)) { - $_sort1 = false; - $_sort2 = false; - if (is_array($new_attrs[$attr])) { + if (is_array($old_attrs[$attr]) && is_array($new_attrs[$attr])) { $_sort1 = $new_attrs[$attr]; sort($_sort1); - } - if (is_array($old_attr_value)) { $_sort2 = $old_attr_value; sort($_sort2); + } else { + $_sort1 = true; + $_sort2 = false; } if (!($new_attrs[$attr] === $old_attr_value) && !($_sort1 === $_sort2)) { //console("Attribute $attr changed from", $old_attr_value, "to", $new_attrs[$attr]); if ($attr === $rdn_attr) { - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr]; + //console("This attribute is the RDN attribute. Let's see if it is multi-valued, and if the original still exists in the new value."); + if (is_array($old_attrs[$attr])) { + if (!is_array($new_attrs[$attr])) { + if (in_array($new_attrs[$attr], $old_attrs[$attr])) { + // TODO: Need to remove all $old_attrs[$attr] values not equal to $new_attrs[$attr], and not equal to the current $rdn_attr value [0] + + //console("old attrs. is array, new attrs. is not array. new attr. exists in old attrs."); + + $rdn_attr_value = array_shift($old_attrs[$attr]); + $_attr_to_remove = array(); + + foreach ($old_attrs[$attr] as $value) { + if (strtolower($value) != strtolower($new_attrs[$attr])) { + $_attr_to_remove[] = $value; + } + } + + //console("Adding to delete attribute $attr values:" . implode(', ', $_attr_to_remove)); + + $mod_array['delete'][$attr] = $_attr_to_remove; + + if (strtolower($new_attrs[$attr]) !== strtolower($rdn_attr_value)) { + //console("new attrs is not the same as the old rdn value, issuing a rename"); + $mod_array['rename']['dn'] = $subject_dn; + $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr][0]; + } + + } else { + //console("new attrs is not the same as any of the old rdn value, issuing a full rename"); + $mod_array['rename']['dn'] = $subject_dn; + $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr]; + } + } else { + // TODO: See if the rdn attr. value is still in $new_attrs[$attr] + if (in_array($old_attrs[$attr][0], $new_attrs[$attr])) { + //console("Simply replacing attr $attr as rnd attr value is preserved."); + $mod_array['replace'][$attr] = $new_attrs[$attr]; + } else { + // TODO: This fails. + $mod_array['rename']['dn'] = $subject_dn; + $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr][0]; + $mod_array['delete'][$attr] = $old_attrs[$attr][0]; + } + } + } else { + if (!is_array($new_attrs[$attr])) { + //console("Renaming " . $old_attrs[$attr] . " to " . $new_attrs[$attr]); + $mod_array['rename']['dn'] = $subject_dn; + $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr]; + } else { + //console("Adding to replace"); + // An additional attribute value is being supplied. Just replace and continue. + $mod_array['replace'][$attr] = $new_attrs[$attr]; + continue; + } + } + } else { if (empty($new_attrs[$attr])) { switch ($attr) { @@ -1095,7 +1353,7 @@ class LDAP $old_ou = implode(',', $subject_dn_components); } - if (!(strtolower($old_ou) === strtolower($new_ou))) { + if (!(empty($old_ou) || empty($new_ou)) && !(strtolower($old_ou) === strtolower($new_ou))) { $mod_array['rename']['new_parent'] = $new_ou; if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) { $mod_array['rename']['dn'] = $subject_dn; @@ -1117,15 +1375,43 @@ class LDAP { $this->_bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw); + //console($attributes); + // Opportunities to set false include failed ldap commands. $result = true; + if (is_array($attributes['rename']) && !empty($attributes['rename'])) { + $olddn = $attributes['rename']['dn']; + $newrdn = $attributes['rename']['new_rdn']; + if (!empty($attributes['rename']['new_parent'])) { + $new_parent = $attributes['rename']['new_parent']; + } else { + $new_parent = null; + } + + //console("Attempt to rename $olddn to $newrdn,$new_parent"); + + $result = ldap_rename($this->conn, $olddn, $newrdn, $new_parent, true); + if ($result) { + if ($new_parent) { + $subject_dn = $newrdn . ',' . $new_parent; + } else { + $old_parent_dn_components = ldap_explode_dn($olddn, 0); + unset($old_parent_dn_components["count"]); + $old_rdn = array_shift($old_parent_dn_components); + $old_parent_dn = implode(",", $old_parent_dn_components); + $subject_dn = $newrdn . ',' . $old_parent_dn; + } + } + + } + if (is_array($attributes['replace']) && !empty($attributes['replace'])) { $result = ldap_mod_replace($this->conn, $subject_dn, $attributes['replace']); } if (!$result) { - //console("Failed to replace the following attributes", $attributes['replace']); + //console("Failed to replace the following attributes on subject " . $subject_dn, $attributes['replace']); return false; } @@ -1148,22 +1434,8 @@ class LDAP return false; } - if (is_array($attributes['rename']) && !empty($attributes['rename'])) { - $olddn = $attributes['rename']['dn']; - $newrdn = $attributes['rename']['new_rdn']; - if (!empty($attributes['rename']['new_parent'])) { - $new_parent = $attributes['rename']['new_parent']; - } else { - $new_parent = null; - } - - //console("Attempt to rename $olddn to $newrdn,$new_parent"); - - $result = ldap_rename($this->conn, $olddn, $newrdn, $new_parent, true); - } - if (!$result) { - error_log("LDAP Error: " . $this->_errstr()); + //console("LDAP Error: " . $this->_errstr()); return false; } @@ -1248,6 +1520,32 @@ class LDAP return $result['']['supportedcontrol']; } + private function resources_list($attributes = array(), $search = array()) + { + $conf = Conf::get_instance(); + + $base_dn = $conf->get('resource_base_dn'); + + if (!$base_dn) + $base_dn = "ou=Resources," . $conf->get('base_dn'); + + $filter = $conf->get('resource_filter'); + if (!$filter) + $filter = '(objectclass=*)'; + + if (empty($attributes) || !is_array($attributes)) { + $attributes = array('*'); + } + + if ($s_filter = $this->_search_filter($search)) { + // join search filter with objectClass filter + $filter = '(&' . $filter . $s_filter . ')'; + } + + return $this->_search($base_dn, $filter, $attributes); + } + + private function users_list($attributes = array(), $search = array()) { $conf = Conf::get_instance(); @@ -1442,6 +1740,33 @@ class LDAP return true; } + private function _domain_add_alias($domain, $parent) + { + $conf = Conf::get_instance(); + $domain_base_dn = $conf->get('ldap', 'domain_base_dn'); + $domain_filter = $conf->get('ldap', 'domain_filter'); + + $domain_name_attribute = $conf->get('ldap', 'domain_name_attribute'); + + $domain_filter = '(&(' . $domain_name_attribute . '=' . $parent . ')' . $domain_filter . ')'; + + $domain_entry = self::normalize_result($this->_search($domain_base_dn, $domain_filter)); + + // TODO: Catch not having found any such parent domain + + $domain_dn = key($domain_entry); + + // private function modify_entry($subject_dn, $old_attrs, $new_attrs) + + $_old_attr = array($domain_name_attribute => $domain_entry[$domain_dn][$domain_name_attribute]); + $_new_attr = array($domain_name_attribute => array($domain_entry[$domain_dn][$domain_name_attribute], $domain)); + + return $this->modify_entry($domain_dn, $_old_attr, $_new_attr); + + + + } + /** * Shortcut to ldap_bind() */ @@ -1458,13 +1783,13 @@ class LDAP } // TODO: Debug logging - error_log("->_bind() Binding with $dn"); + //console("->_bind() Binding with $dn"); $this->bind_dn = $dn; $this->bind_pw = $pw; if (($bind_ok = ldap_bind($this->conn, $dn, $pw)) == false) { - error_log("LDAP Error: " . $this->_errstr()); + //console("LDAP Error: " . $this->_errstr()); // Issue error message return false; } @@ -1481,14 +1806,16 @@ class LDAP return true; } + ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 9); + // TODO: Debug logging - error_log("Connecting to " . $this->_ldap_server . " on port " . $this->_ldap_port); + //console("Connecting to " . $this->_ldap_server . " on port " . $this->_ldap_port); $connection = ldap_connect($this->_ldap_server, $this->_ldap_port); if ($connection == false) { $this->conn = null; // TODO: Debug logging - error_log("Not connected: " . ldap_err2str() . "(no.) " . ldap_errno()); + //console("Not connected: " . ldap_err2str() . "(no.) " . ldap_errno()); return false; } @@ -1497,7 +1824,7 @@ class LDAP ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3); // TODO: Debug logging - error_log("Connected!"); + //console("Connected!"); return true; } @@ -1580,6 +1907,11 @@ class LDAP return $ldap_entries; } + private function _search($base_dn, $search_filter = '(objectClass=*)', $attributes = array('*')) + { + return $this->__search($base_dn, $search_filter, $attributes); + } + /** * Shortcut to ldap_search() */ @@ -1600,15 +1932,17 @@ class LDAP } if (($search_results = @ldap_search($this->conn, $base_dn, $search_filter, $attributes)) == false) { - //message("Could not search in " . __METHOD__ . " in " . __FILE__ . " on line " . __LINE__ . ": " . $this->_errstr()); + //console("Could not search in " . __METHOD__ . " in " . __FILE__ . " on line " . __LINE__ . ": " . $this->_errstr()); return false; } if (($entries = ldap_get_entries($this->conn, $search_results)) == false) { - //message("Could not get the results of the search: " . $this->_errstr()); + //console("Could not get the results of the search: " . $this->_errstr()); return false; } + //console("__search() entries:", $entries); + return $entries; } @@ -1704,13 +2038,13 @@ class LDAP */ private function _probe_root_dn($entry_root_dn) { - error_log("Running for entry root dn: " . $entry_root_dn); + //console("Running for entry root dn: " . $entry_root_dn); if (($tmpconn = ldap_connect($this->_ldap_server)) == false) { //message("LDAP Error: " . $this->_errstr()); return false; } - error_log("User DN: " . $_SESSION['user']->user_bind_dn); + //console("User DN: " . $_SESSION['user']->user_bind_dn); if (($bind_success = ldap_bind($tmpconn, $_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw)) == false) { //message("LDAP Error: " . $this->_errstr()); @@ -1771,7 +2105,7 @@ class LDAP $this->_bind($this->conf->get('manager_bind_dn'), $this->conf->get('manager_bind_pw')); } - error_log("Searching for a group dn in $root_dn, with search filter: $search_filter"); + //console("Searching for a group dn in $root_dn, with search filter: $search_filter"); $search_results = ldap_search($this->conn, $root_dn, $search_filter); @@ -1795,7 +2129,7 @@ class LDAP $this->_bind($this->conf->get('manager_bind_dn'), $this->conf->get('manager_bind_pw')); } - error_log("Searching for a user dn in $root_dn, with search filter: $search_filter"); + //console("Searching for a user dn in $root_dn, with search filter: $search_filter"); $search_results = ldap_search($this->conn, $root_dn, $search_filter); @@ -1819,10 +2153,10 @@ class LDAP if (is_array($entry) && in_array('objectclass', $entry)) { if (!in_array(array('groupofnames', 'groupofuniquenames', 'groupofurls'), $entry['objectclass'])) { - error_log("Called _list_groups_members on a non-group!"); + //console("Called _list_groups_members on a non-group!"); } else { - error_log("Called list_group_members(" . $dn . ")"); + //console("Called list_group_members(" . $dn . ")"); } } @@ -1855,7 +2189,7 @@ class LDAP private function _list_group_member($dn, $members, $recurse = true) { - error_log("Called _list_group_member(" . $dn . ")"); + //console("Called _list_group_member(" . $dn . ")"); $group_members = array(); @@ -1932,7 +2266,7 @@ class LDAP private function _list_group_memberurl($dn, $memberurls, $recurse = true) { - error_log("Called _list_group_memberurl(" . $dn . ")"); + //console("Called _list_group_memberurl(" . $dn . ")"); // Use the member attributes to return an array of member ldap objects // NOTE that the member attribute is supposed to contain a DN @@ -1946,7 +2280,7 @@ class LDAP foreach ($entries as $entry_dn => $_entry) { $group_members[$entry_dn] = $_entry; - error_log("Found " . $entry_dn); + //console("Found " . $entry_dn); if ($recurse) { // Nested group @@ -1970,7 +2304,7 @@ class LDAP */ private function _parse_memberurl($url) { - error_log("Parsing URL: " . $url); + //console("Parsing URL: " . $url); preg_match('/(.*):\/\/(.*)\/(.*)\?(.*)\?(.*)\?(.*)/', $url, $matches); return $matches; } diff --git a/lib/Conf.php b/lib/Conf.php index 4f1f883..e91110c 100644 --- a/lib/Conf.php +++ b/lib/Conf.php @@ -26,8 +26,15 @@ class Conf { static private $instance; + private $_conf = array(); + const CONFIG_FILE = '/etc/kolab/kolab.conf'; + const STRING = 0; + const BOOL = 1; + const INT = 2; + const FLOAT = 3; + /** * This implements the 'singleton' design pattern * @@ -49,38 +56,28 @@ class Conf { return; } - $_ini_raw = file(self::CONFIG_FILE); - - $this->_conf = array(); + $this->read_config(); - foreach ($_ini_raw as $_line) { - if (preg_match('/^\[([a-z0-9-_\.]+)\]/', $_line, $matches)) { - $_cur_section = $matches[1]; - $this->_conf[$_cur_section] = array(); - unset($_cur_key); - } + } - if (preg_match('/^;/', $_line, $matches)) { - } + public function get($key1, $key2 = null, $type = null) + { + $value = $this->expand($this->get_raw($key1, $key2)); - if (preg_match('/^([a-z0-9\.-_]+)\s*=\s*(.*)/', $_line, $matches)) { - if (isset($_cur_section) && !empty($_cur_section)) { - $_cur_key = $matches[1]; - $this->_conf[$_cur_section][$matches[1]] = isset($matches[2]) ? $matches[2] : ''; - } - } + if ($value === null) { + return $value; + } - if (preg_match('/^\s+(.*)$/', $_line, $matches)) { - if (isset($_cur_key) && !empty($_cur_key)) { - $this->_conf[$_cur_section][$_cur_key] .= $matches[1]; - } - } + switch ($type) { + case self::INT: + return intval($value); + case self::FLOAT: + return floatval($value); + case self::BOOL: + return (bool) preg_match('/^(true|1|on|enabled|yes)$/i', $value); } - } - public function get($key1, $key2 = NULL) - { - return $this->expand($this->get_raw($key1, $key2)); + return (string) $value; } public function get_list($key1, $key2 = NULL) @@ -130,7 +127,7 @@ class Conf { return $this->_conf[$domain_section_name][$key1]; } } catch (Exception $e) { - $domain_section_name = $this->get('kolab', 'primary_domain'); + $domain_section_name = $this->get_raw('kolab', 'primary_domain'); if (isset($this->_conf[$domain_section_name][$key1])) { return $this->_conf[$domain_section_name][$key1]; } @@ -154,7 +151,7 @@ class Conf { // error_log("Could not find setting for \$key1: " . $key1 . // " with \$key2: " . $key2); - return false; + return null; } public function expand($str, $custom = FALSE) @@ -183,4 +180,35 @@ class Conf { return $str; } } + + private function read_config() + { + $_ini_raw = file(self::CONFIG_FILE); + + $this->_conf = array(); + + foreach ($_ini_raw as $_line) { + if (preg_match('/^\[([a-z0-9-_\.]+)\]/', $_line, $matches)) { + $_cur_section = $matches[1]; + $this->_conf[$_cur_section] = array(); + unset($_cur_key); + } + + if (preg_match('/^;/', $_line, $matches)) { + } + + if (preg_match('/^([a-z0-9\.-_]+)\s*=\s*(.*)/', $_line, $matches)) { + if (isset($_cur_section) && !empty($_cur_section)) { + $_cur_key = $matches[1]; + $this->_conf[$_cur_section][$matches[1]] = isset($matches[2]) ? $matches[2] : ''; + } + } + + if (preg_match('/^\s+(.*)$/', $_line, $matches)) { + if (isset($_cur_key) && !empty($_cur_key)) { + $this->_conf[$_cur_section][$_cur_key] .= $matches[1]; + } + } + } + } } diff --git a/lib/User.php b/lib/User.php index d4f8ca0..ea6ae0d 100644 --- a/lib/User.php +++ b/lib/User.php @@ -97,7 +97,7 @@ class User public function groups() { - //error_log("Called " . __FUNCTION__ . " on line " . __LINE__ . " of " . __FILE__); + //console("Called " . __FUNCTION__ . " on line " . __LINE__ . " of " . __FILE__); //debug_print_backtrace(); if ($this->_groups || (is_array($this->_groups) && count($this->_groups) >= 1)) { diff --git a/lib/api/kolab_api_service_domain.php b/lib/api/kolab_api_service_domain.php new file mode 100644 index 0000000..e00de8f --- /dev/null +++ b/lib/api/kolab_api_service_domain.php @@ -0,0 +1,145 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + | Author: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +/** + * Service providing domain mutations + */ +class kolab_api_service_domain extends kolab_api_service +{ + + /** + * Returns service capabilities. + * + * @param string $domain Domain name + * + * @return array Capabilities list + */ + public function capabilities($domain) + { + return array( + 'add' => 'w', + 'edit' => 'w', + 'delete' => 'w' + ); + } + + public function domain_add($getdata, $postdata) + { + if (empty($postdata['domain'])) { + return; + } + + if (empty($postdata['parent'])) { + return; + } + + $auth = Auth::get_instance(); + $auth->domain_add($postdata['domain'], $postdata['parent']); + } + + public function domain_edit($getdata, $postdata) + { + //console("domain_edit \$postdata", $postdata); + + $domain_attributes = $this->parse_input_attributes('domain', $postdata); + $domain = $postdata['id']; + + $auth = Auth::get_instance(); + $result = $auth->domain_edit($postdata['id'], $domain_attributes, $postdata['type_id']); + + // @TODO: return unique attribute or all attributes as domain_add() + if ($result) { + return true; + } + + return false; + } + + public function domain_effective_rights($getdata, $postdata) + { + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + + //console($getdata); + + if (!empty($getdata['domain'])) { + $entry_dn = $getdata['domain']; + + $unique_attr = $conf->get('ldap', 'unique_attribute'); + + $domain = $auth->domain_find_by_attribute( + array($unique_attr => $entry_dn) + ); + + //console($domain); + + if (!empty($domain)) { + $entry_dn = key($domain); + } + + } else { + $conf = Conf::get_instance(); + $entry_dn = $conf->get('ldap', 'domain_base_dn'); + } + + //console("API/domain.effective_rights(); Using entry_dn: " . $entry_dn); + + // TODO: Fix searching the correct base_dn... Perhaps find the entry + // first. + $effective_rights = $auth->list_rights($entry_dn); + + //console($effective_rights); + return $effective_rights; + } + + /** + * Domain information. + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return array|bool Domain attributes, False on error + */ + public function domain_info($getdata, $postdata) + { + if (!isset($getdata['domain'])) { + return false; + } + + $auth = Auth::get_instance(); + $result = $auth->domain_info($getdata['domain']); + + // normalize result + $result = $this->parse_result_attributes('domain', $result); + + //console("API/domain.info() \$result:", $result); + + if ($result) { + return $result; + } + + return false; + } +} diff --git a/lib/api/kolab_api_service_domains.php b/lib/api/kolab_api_service_domains.php index e964936..439e865 100644 --- a/lib/api/kolab_api_service_domains.php +++ b/lib/api/kolab_api_service_domains.php @@ -56,6 +56,7 @@ class kolab_api_service_domains extends kolab_api_service $auth = Auth::get_instance(); $domains = $auth->list_domains(); + //console($domains); $count = count($domains); // pagination diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php index 614bd18..4aa7b78 100644 --- a/lib/api/kolab_api_service_form_value.php +++ b/lib/api/kolab_api_service_form_value.php @@ -88,35 +88,49 @@ class kolab_api_service_form_value extends kolab_api_service } /** - * Validation of field values. + * Generation of values for fields of type LIST. * * @param array $getdata GET parameters * @param array $postdata POST parameters. Required parameters: + * - attribute: attribute name * - type_id: Type identifier * - object_type: Object type (user, group, etc.) * * @return array Response with attribute name as a key */ - public function validate($getdata, $postdata) + public function list_options($getdata, $postdata) { - $attribs = $this->object_type_attributes($postdata['object_type'], $postdata['type_id']); - $result = array(); + //console($postdata); - foreach ((array)$postdata as $attr_name => $attr_value) { - if (empty($attr_name) || $attr_name == 'type_id' || $attr_name == 'object_type') { - continue; - } + $attribs = $this->object_type_attributes($postdata['object_type'], $postdata['type_id']); + $attr_name = $postdata['attribute']; + $result = array( + // return search value, so client can match response to request + 'search' => $postdata['search'], + 'list' => array(), + ); - $method_name = 'validate_' . strtolower($attr_name); + if (empty($attr_name)) { + return $result; + } + + + $method_name = 'list_options_' . strtolower($attr_name) . '_' . strtolower($postdata['object_type']); + + if (!method_exists($this, $method_name)) { + //console("Method $method_name doesn't exist"); + + $method_name = 'list_options_' . strtolower($attr_name); if (!method_exists($this, $method_name)) { - $result[$attr_name] = 'OK'; - continue; + return $result; } - - $result[$attr_name] = $this->{$method_name}($attr_value); } + //console($method_name); + + $result['list'] = $this->{$method_name}($postdata, $attribs); + return $result; } @@ -158,43 +172,42 @@ class kolab_api_service_form_value extends kolab_api_service } /** - * Generation of values for fields of type LIST. + * Validation of field values. * * @param array $getdata GET parameters * @param array $postdata POST parameters. Required parameters: - * - attribute: attribute name * - type_id: Type identifier * - object_type: Object type (user, group, etc.) * * @return array Response with attribute name as a key */ - public function list_options($getdata, $postdata) + public function validate($getdata, $postdata) { - //console($postdata); + console("Executing validate() for \$getdata, \$postdata", $getdata, $postdata); - $attribs = $this->object_type_attributes($postdata['object_type'], $postdata['type_id']); - $attr_name = $postdata['attribute']; - $result = array( - // return search value, so client can match response to request - 'search' => $postdata['search'], - 'list' => array(), - ); + $attribs = $this->object_type_attributes($postdata['object_type'], $postdata['type_id']); + $result = array(); - if (empty($attr_name)) { - return $result; - } + foreach ((array)$postdata as $attr_name => $attr_value) { + if (empty($attr_name) || $attr_name == 'type_id' || $attr_name == 'object_type') { + continue; + } - $method_name = 'list_options_' . strtolower($attr_name); + $method_name = 'validate_' . strtolower($attr_name) . '_' . strtolower($postdata['object_type']); - //console($method_name); + if (!method_exists($this, $method_name)) { + //console("Method $method_name doesn't exist"); - if (!method_exists($this, $method_name)) { - return $result; - } + $method_name = 'validate_' . strtolower($attr_name); - //console("Still here"); + if (!method_exists($this, $method_name)) { + $result[$attr_name] = 'OK'; + continue; + } + } - $result['list'] = $this->{$method_name}($postdata, $attribs); + $result[$attr_name] = $this->{$method_name}($attr_value); + } return $result; } @@ -221,6 +234,46 @@ class kolab_api_service_form_value extends kolab_api_service } } + private function generate_cn_resource($postdata, $attribs = array()) + { + if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['cn'])) { + // Use Data Please + foreach ($attribs['auto_form_fields']['cn']['data'] as $key) { + if (!isset($postdata[$key])) { + throw new Exception("Key not set: " . $key, 12356); + } + } + + $auth = Auth::get_instance($_SESSION['user']->get_domain()); + $conf = Conf::get_instance(); + + $unique_attr = $conf->get('unique_attribute'); + if (!$unique_attr) { + $unique_attr = 'nsuniqueid'; + } + + $cn = $postdata['cn']; + + $x = 2; + while (($resource_found = $auth->resource_find_by_attribute(array('cn' => $cn)))) { + if (!empty($postdata['id'])) { + $resource_found_dn = key($resource_found); + $resource_found_unique_attr = $auth->get_attribute($resource_found_dn, $unique_attr); + //console("resource with mail $mail found", $resource_found_unique_attr); + if ($resource_found_unique_attr == $postdata['id']) { + //console("that's us."); + break; + } + } + + $cn = $postdata['cn'] . ' #' . $x; + $x++; + } + + return $cn; + } + } + private function generate_displayname($postdata, $attribs = array()) { if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['displayname'])) { @@ -314,6 +367,24 @@ class kolab_api_service_form_value extends kolab_api_service } } + private function generate_kolabtargetfolder_resource($postdata, $attribs = array()) + { + if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['kolabtargetfolder'])) { + // Use Data Please + foreach ($attribs['auto_form_fields']['kolabtargetfolder']['data'] as $key) { + if (!isset($postdata[$key])) { + throw new Exception("Key not set: " . $key, 12356); + } + } + + // TODO: Detect or from config + $imap_hierarchysep = '/'; + $cn = $this->generate_cn_resource($postdata, $attribs); + + return 'shared' . $imap_hierarchysep . 'Resources' . $imap_hierarchysep . $cn . '@' . $_SESSION['user']->get_domain(); + } + } + private function generate_mail($postdata, $attribs = array()) { return $this->generate_primary_mail($postdata, $attribs); @@ -324,6 +395,61 @@ class kolab_api_service_form_value extends kolab_api_service return $this->generate_primary_mail_group($postdata, $attribs); } + private function generate_mail_resource($postdata, $attribs = array()) + { + $db = SQL::get_instance(); + $result = $db->fetch_assoc($db->query("SELECT `key` FROM `resource_types` WHERE id = ?", $postdata['type_id'])); + + $object_type_key = $result['key']; + + if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['mail'])) { + // Use Data Please + foreach ($attribs['auto_form_fields']['mail']['data'] as $key) { + if (!isset($postdata[$key])) { + throw new Exception("Key not set: " . $key, 12356); + } + } + + $resourcedata = kolab_recipient_policy::normalize_groupdata($postdata); + //console("normalized resource data", $resourcedata); + + // TODO: Normalize $postdata + $mail_local = 'resource-' . $object_type_key . '-' . strtolower($resourcedata['cn']); + $mail_domain = $_SESSION['user']->get_domain(); + $mail = $mail_local . '@' . $mail_domain; + + $orig_mail = $mail; + + $auth = Auth::get_instance($_SESSION['user']->get_domain()); + $conf = Conf::get_instance(); + + $unique_attr = $conf->get('unique_attribute'); + if (!$unique_attr) { + $unique_attr = 'nsuniqueid'; + } + + $x = 2; + while (($resource_found = $auth->resource_find_by_attribute(array('mail' => $mail)))) { + if (!empty($postdata['id'])) { + $resource_found_dn = key($resource_found); + $resource_found_unique_attr = $auth->get_attribute($resource_found_dn, $unique_attr); + //console("resource with mail $mail found", $resource_found_unique_attr); + if ($resource_found_unique_attr == $postdata['id']) { + //console("that's us."); + break; + } + } + + $mail = $mail_local . '-' . $x . '@' . $mail_domain; + $x++; + } + + return $mail; + + + } + } + private function generate_mailalternateaddress($postdata, $attribs = array()) { return $this->generate_secondary_mail($postdata, $attribs); @@ -389,7 +515,7 @@ class kolab_api_service_form_value extends kolab_api_service private function generate_secondary_mail($postdata, $attribs = array()) { - $secondary_mail_address = Array(); + $secondary_mail_addresses = Array(); if (isset($attribs['auto_form_fields'])) { if (isset($attribs['auto_form_fields']['alias'])) { @@ -412,9 +538,17 @@ class kolab_api_service_form_value extends kolab_api_service } } - $secondary_mail = kolab_recipient_policy::secondary_mail($postdata); + if (array_key_exists('mail', $attribs['auto_form_fields'])) { + if (!array_key_exists('mail', $postdata)) { + $postdata['mail'] = $this->generate_primary_mail($postdata, $attribs); + } + } + + $secondary_mail_addresses = kolab_recipient_policy::secondary_mail($postdata); - return $secondary_mail; + // TODO: Check for uniqueness. Not sure what to do if not unique. + + return $secondary_mail_addresses; } } @@ -434,7 +568,7 @@ class kolab_api_service_form_value extends kolab_api_service setlocale(LC_ALL, $postdata['preferredlanguage']); } /* else { - console("No locale specified...!"); + //console("No locale specified...!"); } */ @@ -446,7 +580,7 @@ class kolab_api_service_form_value extends kolab_api_service $auth = Auth::get_instance($_SESSION['user']->get_domain()); $conf = Conf::get_instance(); - + $unique_attr = $conf->get('unique_attribute'); if (!$unique_attr) { $unique_attr = 'nsuniqueid'; @@ -459,6 +593,7 @@ class kolab_api_service_form_value extends kolab_api_service $user_found_unique_attr = $auth->get_attribute($user_found_dn, $unique_attr); //console("user with uid $uid found", $user_found_unique_attr); if ($user_found_unique_attr == $postdata['id']) { + //console("that's us."); break; } } @@ -560,7 +695,7 @@ class kolab_api_service_form_value extends kolab_api_service private function list_options_nsrole($postdata, $attribs = array()) { - error_log("Listing options for attribute 'nsrole', while the expected attribute to use is 'nsroledn'"); + //console("Listing options for attribute 'nsrole', while the expected attribute to use is 'nsroledn'"); return $this->list_options_nsroledn($postdata, $attribs); } @@ -605,6 +740,11 @@ class kolab_api_service_form_value extends kolab_api_service return $this->_list_options_members($postdata, $attribs); } + private function list_options_uniquemember_resource($postdata, $attribs = array()) + { + return $this->_list_options_resources($postdata, $attribs); + } + private function select_options_c($postdata, $attribs = array()) { return $this->_select_options_from_db('c'); @@ -670,22 +810,88 @@ class kolab_api_service_form_value extends kolab_api_service return $options; } - private function _select_options_from_db($attribute) + private function validate_alias($value) { + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + if (!is_array($value)) { + $value = (array)($value); + } - if (empty($attribute)) { - return false; + foreach ($value as $mail_address) { + if (!$this->_validate_email_address($mail_address)) { + throw new Exception("Invalid email address '$mail_address'", 692); + } + + // Only validate the 'alias' attribute is in any of my domain name + // spaces if indeed it is listed as a mail attribute. + if (in_array('alias', $conf->get_list('mail_attributes'))) { + if (!$this->_validate_email_address_in_any_of_my_domains($mail_address)) { + throw new Exception("Email address '$mail_address' not in local domain", 693); + } + } } - $db = SQL::get_instance(); - $result = $db->fetch_assoc($db->query("SELECT option_values FROM options WHERE attribute = ?", $attribute)); + } - $result = json_decode($result['option_values']); + private function validate_mail($value) + { + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + if (!is_array($value)) { + $value = (array)($value); + } - if (empty($result)) { - return false; + foreach ($value as $mail_address) { + if (!$this->_validate_email_address($mail_address)) { + throw new Exception("Invalid email address '$mail_address'", 692); + } + + // Only validate the 'mail' attribute is in any of my domain name + // spaces if indeed it is listed as a mail attribute. + if (in_array('mail', $conf->get_list('mail_attributes'))) { + if (!$this->_validate_email_address_in_any_of_my_domains($mail_address)) { + throw new Exception("Email address '$mail_address' not in local domain", 693); + } + } + } + } + + private function validate_mailquota($value) + { + return (int)($value); + } + + private function validate_mailalternateaddress($value) + { + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + if (!is_array($value)) { + $value = (array)($value); + } + + foreach ($value as $mail_address) { + if (!$this->_validate_email_address($mail_address)) { + throw new Exception("Invalid email address '$mail_address'", 692); + } + + // Only validate the 'mailalternateaddress' attribute is in any of my domain name + // spaces if indeed it is listed as a mail attribute. + if (in_array('mailalternateaddress', $conf->get_list('mail_attributes'))) { + if (!$this->_validate_email_address_in_any_of_my_domains($mail_address)) { + throw new Exception("Email address '$mail_address' not in local domain", 693); + } + } + } + } + + private function _highest_of_two($one, $two) { + if ($one > $two) { + return $one; + } elseif ($one == $two) { + return $one; } else { - return $result; + return $two; } } @@ -730,7 +936,7 @@ class kolab_api_service_form_value extends kolab_api_service } elseif (!empty($value['cn'])) { $list[$idx] = $value['cn']; } else { - console("No display name or cn for $idx"); + //console("No display name or cn for $idx"); } if (!empty($value['mail'])) { @@ -741,13 +947,155 @@ class kolab_api_service_form_value extends kolab_api_service return $list; } - private function _highest_of_two($one, $two) { - if ($one > $two) { - return $one; - } elseif ($one == $two) { - return $one; + private function _list_options_resources($postdata, $attribs = array()) + { + // return specified records only, by exact DN attributes + if (!empty($postdata['list'])) { + $data['search'] = array( + 'entrydn' => array( + 'value' => $postdata['list'], + 'type' => 'exact', + ), + ); + } + // return records with specified string + else { + $keyword = array('value' => $postdata['search']); + $data['page_size'] = 15; + $data['search'] = array( + 'cn' => $keyword, + ); + } + + $data['attributes'] = array('cn'); + + //console("api/form_value._list_options_resources() searching with data", $data); + + $service = $this->controller->get_service('resources'); + $result = $service->resources_list(null, $data); + $list = $result['list']; + + // convert to key=>value array + foreach ($list as $idx => $value) { + if (!empty($value['displayname'])) { + $list[$idx] = $value['displayname']; + } elseif (!empty($value['cn'])) { + $list[$idx] = $value['cn']; + } else { + //console("No display name or cn for $idx"); + } + + } + + return $list; + } + + private function _select_options_from_db($attribute) + { + + if (empty($attribute)) { + return false; + } + + $db = SQL::get_instance(); + $result = $db->fetch_assoc($db->query("SELECT option_values FROM options WHERE attribute = ?", $attribute)); + + $result = json_decode($result['option_values']); + + if (empty($result)) { + return false; } else { - return $two; + return $result; + } + } + + private function _validate_email_address($mail_address) { + $valid = true; + + $at_index = strrpos($mail_address, "@"); + if (is_bool($at_index) && !$at_index) { + $valid = false; + + } else { + $domain = substr($mail_address, $at_index+1); + $local = substr($mail_address, 0, $at_index); + + if (strlen($local) < 1 || strlen($local) > 64) { + // local part length exceeded + $valid = false; + + } else if (strlen($domain) < 1 || strlen($domain) > 255) { + // domain part length exceeded + $valid = false; + + } else if ($local[0] == '.' || $local[strlen($local)-1] == '.') { + // local part starts or ends with '.' + $valid = false; + + } else if (preg_match('/\\.\\./', $local)) { + // local part has two consecutive dots + $valid = false; + + } else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { + // character not valid in domain part + $valid = false; + + } else if (preg_match('/\\.\\./', $domain)) { + // domain part has two consecutive dots + $valid = false; + + } else if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\","",$local))) { + // character not valid in local part unless + // local part is quoted + if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\","",$local))) { + $valid = false; + } + } + + if ($valid && !(checkdnsrr($domain,"MX") || checkdnsrr($domain,"A"))) { + // domain not found in DNS + $valid = false; + } + } + + return $valid; + } + + private function _validate_email_address_in_any_of_my_domains($mail_address) { + $valid = false; + + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + + $my_primary_domain = $_SESSION['user']->get_domain(); + $all_domains = $auth->list_domains(); + + $valid_domains = array(); + + $dna = $conf->get('domain_name_attribute'); + + $at_index = strrpos($mail_address, "@"); + if (is_bool($at_index) && !$at_index) { + throw new Exception("Invalid email address: No domain name space", 235); + } else { + $email_domain = substr($mail_address, $at_index+1); } + + foreach ($all_domains as $domain_id => $domain_attrs) { + if (!is_array($domain_attrs[$dna])) { + $domain_attrs[$dna] = (array)($domain_attrs[$dna]); + } + + if (in_array($my_primary_domain, $domain_attrs[$dna])) { + $valid_domains = array_merge($valid_domains, $domain_attrs[$dna]); + } + } + + if (in_array($email_domain, $valid_domains)) { + $valid = true; + } + + return $valid; } + } diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php index 6e53340..951b98c 100644 --- a/lib/api/kolab_api_service_group.php +++ b/lib/api/kolab_api_service_group.php @@ -179,7 +179,7 @@ class kolab_api_service_group extends kolab_api_service $auth = Auth::get_instance(); if (empty($getdata['group'])) { - error_log("Empty \$getdata['group']"); + //console("Empty \$getdata['group']"); return FALSE; } diff --git a/lib/api/kolab_api_service_resource.php b/lib/api/kolab_api_service_resource.php new file mode 100644 index 0000000..9cd3304 --- /dev/null +++ b/lib/api/kolab_api_service_resource.php @@ -0,0 +1,185 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + | Author: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +/** + * Service providing resource data management + */ +class kolab_api_service_resource extends kolab_api_service +{ + /** + * Returns service capabilities. + * + * @param string $domain Domain name + * + * @return array Capabilities list + */ + public function capabilities($domain) + { + //console("kolab_api_service_group::capabilities"); + + $auth = Auth::get_instance(); + + $effective_rights = $auth->list_rights('resource'); + + //console("effective_rights", $effective_rights); + + $rights = array(); + + if (in_array('add', $effective_rights['entryLevelRights'])) { + $rights['add'] = "w"; + } + + if (in_array('delete', $effective_rights['entryLevelRights'])) { + $rights['delete'] = "w"; + } + + if (in_array('modrdn', $effective_rights['entryLevelRights'])) { + $rights['edit'] = "w"; + } + + if (in_array('read', $effective_rights['entryLevelRights'])) { + $rights['find'] = "r"; + $rights['find_by_any_attribute'] = "r"; + $rights['find_by_attribute'] = "r"; + $rights['find_by_attributes'] = "r"; + $rights['info'] = "r"; + } + + $rights['effective_rights'] = "r"; + + return $rights; + } + + /** + * Create resource. + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return array|bool User attributes or False on error. + */ + public function resource_add($getdata, $postdata) + { + //console("resource_add()", $postdata); + + $resource_attributes = $this->parse_input_attributes('resource', $postdata); + + //console("resource_add()", $resource_attributes); + + // TODO: The cn needs to be unique + $auth = Auth::get_instance(); + $result = $auth->resource_add($resource_attributes, $postdata['type_id']); + + if ($result) { + return $resource_attributes; + } + + return false; + } + + /** + * Detete resource. + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return bool True on success, False on failure + */ + public function resource_delete($getdata, $postdata) + { + //console("resource_delete()", $getdata, $postdata); + if (!isset($postdata['resource'])) { + return false; + } + + // TODO: Input validation + $auth = Auth::get_instance(); + $result = $auth->resource_delete($postdata['resource']); + + if ($result) { + return $result; + } + + return false; + } + + public function resource_edit($getdata, $postdata) + { + //console("\$postdata to resource_edit()", $postdata); + + $resource_attributes = $this->parse_input_attributes('resource', $postdata); + + //console("\$resource_attributes as result from parse_input_attributes", $resource_attributes); + + $resource = $postdata['id']; + + $auth = Auth::get_instance(); + $result = $auth->resource_edit($resource, $resource_attributes, $postdata['type_id']); + + // Return the $mod_array + if ($result) { + return $result; + } + + return false; + + } + + public function resource_effective_rights($getdata, $postdata) + { + $auth = Auth::get_instance(); + $effective_rights = $auth->list_rights($getdata['resource']); + return $effective_rights; + } + + /** + * User information. + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return array|bool User attributes, False on error + */ + public function resource_info($getdata, $postdata) + { + if (!isset($getdata['resource'])) { + return false; + } + + $auth = Auth::get_instance(); + $result = $auth->resource_info($getdata['resource']); + + // normalize result + $result = $this->parse_result_attributes('resource', $result); + + //console($result); + + if ($result) { + return $result; + } + + return false; + } +} diff --git a/lib/api/kolab_api_service_resource_types.php b/lib/api/kolab_api_service_resource_types.php new file mode 100644 index 0000000..69d13ea --- /dev/null +++ b/lib/api/kolab_api_service_resource_types.php @@ -0,0 +1,64 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + | Author: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +/** + * + */ +class kolab_api_service_resource_types extends kolab_api_service +{ + /** + * Returns service capabilities. + * + * @param string $domain Domain name + * + * @return array Capabilities list + */ + public function capabilities($domain) + { + return array( + 'list' => 'r', + ); + } + + /** + * User types listing. + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return array List result with 'list' and 'count' items + */ + public function resource_types_list($get, $post) + { + $resource_types = $this->object_types('resource'); + + //console("api/resource_types_list()", $resource_types); + + return array( + 'list' => $resource_types, + 'count' => count($resource_types), + ); + } +} diff --git a/lib/api/kolab_api_service_resources.php b/lib/api/kolab_api_service_resources.php new file mode 100644 index 0000000..6bcc7e3 --- /dev/null +++ b/lib/api/kolab_api_service_resources.php @@ -0,0 +1,147 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + | Author: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +/** + * Service providing resources listing + */ +class kolab_api_service_resources extends kolab_api_service +{ + public $list_attribs = array( + 'alias', + 'cn', + 'entrydn', + 'mail', + 'objectclass', + ); + + + /** + * Returns service capabilities. + * + * @param string $domain Domain name + * + * @return array Capabilities list + */ + public function capabilities($domain) + { + return array( + 'list' => 'r', + ); + } + + /** + * Users listing (with searching). + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return array List result with 'list' and 'count' items + */ + public function resources_list($get, $post) + { + $auth = Auth::get_instance(); + + // returned attributes + if (!empty($post['attributes']) && is_array($post['attributes'])) { + // get only supported attributes + $attributes = array_intersect($this->list_attribs, $post['attributes']); + // need to fix array keys + $attributes = array_values($attributes); + } + if (empty($attributes)) { + $attributes = (array)$this->list_attribs[0]; + } + + $search = array(); + $params = array(); + + // searching + if (!empty($post['search']) && is_array($post['search'])) { + $params = $post['search']; + + foreach ($params as $idx => $param) { + // get only supported attributes + if (!in_array($idx, $this->list_attribs)) { + unset($params[$idx]); + continue; + } + + // search string + if (empty($param['value'])) { + unset($params[$idx]); + continue; + } + } + + $search['params'] = $params; + if (!empty($post['search_operator'])) { + $search['operator'] = $post['search_operator']; + } + } + + if (!empty($post['sort_by'])) { + if (is_array($post['sort_by'])) { + $params['sort_by'] = Array(); + foreach ($post['sort_by'] as $attrib) { + if (in_array($attrib, $this->list_attribs)) { + $params['sort_by'][] = $attrib; + } + } + } else { + // check if sort attribute is supported + if (in_array($post['sort_by'], $this->list_attribs)) { + $params['sort_by'] = $post['sort_by']; + } + } + } + + if (!empty($post['sort_order'])) { + $params['sort_order'] = $post['sort_order'] == 'DESC' ? 'DESC' : 'ASC'; + } + + $resources = $auth->list_resources(null, $attributes, $search, $params); + $count = count($resources); + + // pagination + if (!empty($post['page_size']) && $count) { + $size = (int) $post['page_size']; + $page = !empty($post['page']) ? $post['page'] : 1; + $page = max(1, (int) $page); + $offset = ($page - 1) * $size; + + $resources = array_slice($resources, $offset, $size, true); + } + + $result = array( + 'list' => $resources, + 'count' => $count, + ); + + //console($result); + + return $result; + } + +} diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php index 67e7e53..f9adbac 100644 --- a/lib/api/kolab_api_service_user.php +++ b/lib/api/kolab_api_service_user.php @@ -84,7 +84,7 @@ class kolab_api_service_user extends kolab_api_service { //console("user_add()", $postdata); - $user_attributes = $this->parse_input_attributes('user', $postdata); + $user_attributes = $this->parse_input_attributes('user', $postdata); //console("user_add()", $user_attributes); @@ -171,7 +171,7 @@ class kolab_api_service_user extends kolab_api_service $result = $auth->user_info($getdata['user']); // normalize result - $result = $this->parse_result_attributes('user', $result); + $result = $this->parse_result_attributes('user', $result); if ($result) { return $result; diff --git a/lib/client/kolab_client_task_domain.php b/lib/client/kolab_client_task_domain.php new file mode 100644 index 0000000..b8eba82 --- /dev/null +++ b/lib/client/kolab_client_task_domain.php @@ -0,0 +1,350 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +class kolab_client_task_domain extends kolab_client_task +{ + protected $ajax_only = true; + + protected $menu = array( + 'add' => 'domain.add', + ); + + /** + * Default action. + */ + public function action_default() + { + $this->output->set_object('content', 'domain', true); + $this->output->set_object('task_navigation', $this->menu()); + + $this->action_list(); + } + + /** + * Groups list action. + */ + public function action_list() + { + $page_size = 20; + $page = (int) self::get_input('page', 'POST'); + if (!$page || $page < 1) { + $page = 1; + } + + // request parameters + $post = array( + 'attributes' => array('associateddomain'), +// 'sort_order' => 'ASC', + 'sort_by' => 'associateddomain', + 'page_size' => $page_size, + 'page' => $page, + ); + + // search parameters + if (!empty($_POST['search'])) { + $search = self::get_input('search', 'POST', true); + $field = self::get_input('field', 'POST'); + $method = self::get_input('method', 'POST'); + + $search_request = array( + $field => array( + 'value' => $search, + 'type' => $method, + ), + ); + } + else if (!empty($_POST['search_request'])) { + $search_request = self::get_input('search_request', 'POST'); + $search_request = @unserialize(base64_decode($search_request)); + } + + if (!empty($search_request)) { + $post['search'] = $search_request; + $post['search_operator'] = 'OR'; + } + + // get domains list + $result = $this->api->post('domains.list', null, $post); + $count = (int) $result->get('count'); + $result = (array) $result->get('list'); + + // calculate records + if ($count) { + $start = 1 + max(0, $page - 1) * $page_size; + $end = min($start + $page_size - 1, $count); + } + + $rows = $head = $foot = array(); + $cols = array('name'); + $i = 0; + + // table header + $head[0]['cells'][] = array('class' => 'name', 'body' => $this->translate('domain.list')); + + // table footer (navigation) + if ($count) { + $pages = ceil($count / $page_size); + $prev = max(0, $page - 1); + $next = $page < $pages ? $page + 1 : 0; + + $count_str = kolab_html::span(array( + 'content' => $this->translate('domain.list.records', $start, $end, $count)), true); + $prev = kolab_html::a(array( + 'class' => 'prev' . ($prev ? '' : ' disabled'), + 'href' => '#', + 'onclick' => $prev ? "kadm.command('domain.list', {page: $prev})" : "return false", + )); + $next = kolab_html::a(array( + 'class' => 'next' . ($next ? '' : ' disabled'), + 'href' => '#', + 'onclick' => $next ? "kadm.command('domain.list', {page: $next})" : "return false", + )); + + $foot_body = kolab_html::span(array('content' => $prev . $count_str . $next)); + } + $foot[0]['cells'][] = array('class' => 'listnav', 'body' => $foot_body); + + // table body + if (!empty($result)) { + foreach ($result as $idx => $item) { + //console($idx); + if (!is_array($item) || empty($item['associateddomain'])) { + continue; + } + + $i++; + $cells = array(); + + if (is_array($item['associateddomain'])) { + $domain_name = $item['associateddomain'][0]; + } else { + $domain_name = $item['associateddomain']; + } + + $cells[] = array('class' => 'name', 'body' => kolab_html::escape($domain_name), + 'onclick' => "kadm.command('domain.info', '$idx')"); + $rows[] = array('id' => $i, 'class' => 'selectable', 'cells' => $cells); + } + } + else { + $rows[] = array('cells' => array( + 0 => array('class' => 'empty-body', 'body' => $this->translate('domain.norecords') + ))); + } + + $table = kolab_html::table(array( + 'id' => 'domainlist', + 'class' => 'list', + 'head' => $head, + 'body' => $rows, + 'foot' => $foot, + )); + + $this->output->set_env('search_request', $search_request ? base64_encode(serialize($search_request)) : null); + $this->output->set_env('list_page', $page); + $this->output->set_env('list_count', $count); + + $this->watermark('taskcontent'); + $this->output->set_object('domainlist', $table); + } + + /** + * Domain information (form) action. + */ + public function action_info() + { + $id = $this->get_input('id', 'POST'); + //console("action_info() on", $id); + + $result = $this->api->get('domain.info', array('domain' => $id)); + //console("action_info() \$result", $result); + + $domain = $result->get(); + //console("action_info() \$domain", $domain); + + $output = $this->domain_form(array_keys($domain), $domain); + + $this->output->set_object('taskcontent', $output); + } + + /** + * Domain adding (form) action. + */ + public function action_add() + { + $data = $this->get_input('data', 'POST'); + $output = $this->domain_form(null, $data, true); + + $this->output->set_object('taskcontent', $output); + } + + /** + * Domain edit/add form. + */ + private function domain_form($attribs, $data = array()) + { + if (empty($attribs['id'])) { + $attribs['id'] = 'domain-form'; + } + + // Form sections + $sections = array( + 'system' => 'domain.system', + 'other' => 'domain.other', + ); + + // field-to-section map and fields order + $fields_map = array( + 'type_id' => 'system', + 'type_id_name' => 'system', + 'associateddomain' => 'system', + ); + + //console("domain_form() \$data", $data); + + // Prepare fields + list($fields, $types, $type) = $this->form_prepare('domain', $data); + + //console("Result from form_prepare", $fields, $types, $type); + + $add_mode = empty($data['id']); + $accttypes = array(); + + foreach ($types as $idx => $elem) { + $accttypes[$idx] = array('value' => $idx, 'content' => $elem['name']); + } + + // Add domain type id selector + $fields['type_id'] = array( + 'section' => 'system', + 'type' => kolab_form::INPUT_SELECT, + 'options' => $accttypes, + 'onchange' => "kadm.domain_save(true, 'system')", + ); + + // Hide account type selector if there's only one type + if (count($accttypes) < 2 || !$add_mode) { + $fields['type_id']['type'] = kolab_form::INPUT_HIDDEN; + } + + // Create mode + if ($add_mode) { + // Page title + $title = $this->translate('domain.add'); + } + // Edit mode + else { + $title = $data['primary_domain']; + + // Add domain type name + $fields['type_id_name'] = array( + 'label' => 'domain.type_id', + 'section' => 'system', + 'value' => $accttypes[$type]['content'], + ); + } + + // Create form object and populate with fields + $form = $this->form_create('domain', $attribs, $sections, $fields, $fields_map, $data, $add_mode); + + //console("domain_form() \$form", $form); + + $form->set_title(kolab_html::escape($title)); + + $this->output->add_translation('domain.add.success', 'domain.edit.success', 'domain.delete.success'); + + return $form->output(); + } + + /** + * Returns list of domain types. + * + * @return array List of domain types + */ + public function domain_types() + { + $result = array( + 1 => array( + 'key' => 'standard', + 'name' => 'Standard domain', + 'description' => 'A standard domain name space', + 'attributes' => array( + 'auto_form_fields' => array(), + 'form_fields' => array( + 'associateddomain' => array( + 'type' => 'list', + ), + 'inetdomainbasedn' => array( + 'optional' => 'true', + ), + ), + 'fields' => array( + 'objectclass' => array( + 'top', + 'domainrelatedobject', + ), + ), + ), + ), + ); + //console("domain_types() \$result", $result); + return $result; + } + + /** + * Users search form. + * + * @return string HTML output of the form + */ + public function search_form() + { + $form = new kolab_form(array('id' => 'search-form')); + + $form->add_section('criteria', kolab_html::escape($this->translate('search.criteria'))); + $form->add_element(array( + 'section' => 'criteria', + 'label' => $this->translate('search.field'), + 'name' => 'field', + 'type' => kolab_form::INPUT_SELECT, + 'options' => array( + 'cn' => kolab_html::escape($this->translate('search.name')), + 'mail' => kolab_html::escape($this->translate('search.email')), + ), + )); + $form->add_element(array( + 'section' => 'criteria', + 'label' => $this->translate('search.method'), + 'name' => 'method', + 'type' => kolab_form::INPUT_SELECT, + 'options' => array( + 'both' => kolab_html::escape($this->translate('search.contains')), + 'exact' => kolab_html::escape($this->translate('search.is')), + 'prefix' => kolab_html::escape($this->translate('search.prefix')), + ), + )); + + return $form->output(); + } + +} diff --git a/lib/client/kolab_client_task_group.php b/lib/client/kolab_client_task_group.php index 402fabe..0c6e3d0 100644 --- a/lib/client/kolab_client_task_group.php +++ b/lib/client/kolab_client_task_group.php @@ -271,7 +271,7 @@ class kolab_client_task_group extends kolab_client_task } elseif (!empty($value['cn'])) { $list[$idx] = $value['cn']; } else { - console("No display name or cn for $idx"); + //console("No display name or cn for $idx"); } if (!empty($value['mail'])) { @@ -298,6 +298,8 @@ class kolab_client_task_group extends kolab_client_task } } + //console($_SESSION['group_types']); + return $_SESSION['group_types']; } diff --git a/lib/client/kolab_client_task_main.php b/lib/client/kolab_client_task_main.php index b7b4621..5af9754 100644 --- a/lib/client/kolab_client_task_main.php +++ b/lib/client/kolab_client_task_main.php @@ -25,9 +25,12 @@ class kolab_client_task_main extends kolab_client_task { protected $menu = array( - 'user.default' => 'menu.users', - 'group.default' => 'menu.groups', - 'about.default' => 'menu.about', + 'user.default' => 'menu.users', + 'group.default' => 'menu.groups', + 'resource.default' => 'menu.resources', + 'domain.default' => 'menu.domains', + 'role.default' => 'menu.roles', + 'about.default' => 'menu.about', ); diff --git a/lib/client/kolab_client_task_resource.php b/lib/client/kolab_client_task_resource.php new file mode 100644 index 0000000..5e3f7a2 --- /dev/null +++ b/lib/client/kolab_client_task_resource.php @@ -0,0 +1,329 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +class kolab_client_task_resource extends kolab_client_task +{ + protected $ajax_only = true; + + protected $menu = array( + 'add' => 'resource.add', + ); + + /** + * Default action. + */ + public function action_default() + { + $this->output->set_object('content', 'resource', true); + $this->output->set_object('task_navigation', $this->menu()); + + $this->action_list(); + } + + /** + * Users list action. + */ + public function action_list() + { + $page_size = 20; + $page = (int) self::get_input('page', 'POST'); + if (!$page || $page < 1) { + $page = 1; + } + + // request parameters + $post = array( + 'attributes' => array('cn'), +// 'sort_order' => 'ASC', + 'sort_by' => array('cn'), + 'page_size' => $page_size, + 'page' => $page, + ); + + // search parameters + if (!empty($_POST['search'])) { + $search = self::get_input('search', 'POST', true); + $field = self::get_input('field', 'POST'); + $method = self::get_input('method', 'POST'); + + $search_request = array( + $field => array( + 'value' => $search, + 'type' => $method, + ), + ); + } + else if (!empty($_POST['search_request'])) { + $search_request = self::get_input('search_request', 'POST'); + $search_request = @unserialize(base64_decode($search_request)); + } + + if (!empty($search_request)) { + $post['search'] = $search_request; + $post['search_operator'] = 'OR'; + } + + // get resources list + $result = $this->api->post('resources.list', null, $post); + $count = $result->get('count'); + $result = (array) $result->get('list'); + + //console($result); + + // calculate records + if ($count) { + $start = 1 + max(0, $page - 1) * $page_size; + $end = min($start + $page_size - 1, $count); + } + + $rows = $head = $foot = array(); + $cols = array('name'); + $i = 0; + + // table header + $head[0]['cells'][] = array('class' => 'name', 'body' => $this->translate('resource.list')); + + // table footer (navigation) + if ($count) { + $pages = ceil($count / $page_size); + $prev = max(0, $page - 1); + $next = $page < $pages ? $page + 1 : 0; + + $count_str = kolab_html::span(array( + 'content' => $this->translate('resource.list.records', $start, $end, $count)), true); + $prev = kolab_html::a(array( + 'class' => 'prev' . ($prev ? '' : ' disabled'), + 'href' => '#', + 'onclick' => $prev ? "kadm.command('resource.list', {page: $prev})" : "return false", + )); + $next = kolab_html::a(array( + 'class' => 'next' . ($next ? '' : ' disabled'), + 'href' => '#', + 'onclick' => $next ? "kadm.command('resource.list', {page: $next})" : "return false", + )); + + $foot_body = kolab_html::span(array('content' => $prev . $count_str . $next)); + } + $foot[0]['cells'][] = array('class' => 'listnav', 'body' => $foot_body); + + // table body + if (!empty($result)) { + foreach ($result as $idx => $item) { + if (!is_array($item) || empty($item['cn'])) { + continue; + } + + $i++; + $cells = array(); + $cells[] = array('class' => 'name', 'body' => kolab_html::escape($item['cn']), + 'onclick' => "kadm.command('resource.info', '$idx')"); + $rows[] = array('id' => $i, 'class' => 'selectable', 'cells' => $cells); + } + } + else { + $rows[] = array('cells' => array( + 0 => array('class' => 'empty-body', 'body' => $this->translate('resource.norecords') + ))); + } + + $table = kolab_html::table(array( + 'id' => 'resourcelist', + 'class' => 'list', + 'head' => $head, + 'body' => $rows, + 'foot' => $foot, + )); + + $this->output->set_env('search_request', $search_request ? base64_encode(serialize($search_request)) : null); + $this->output->set_env('list_page', $page); + $this->output->set_env('list_count', $count); + + $this->watermark('taskcontent'); + $this->output->set_object('resourcelist', $table); + } + + /** + * Resource adding (form) action. + */ + public function action_add() + { + $data = $this->get_input('data', 'POST'); + $output = $this->resource_form(null, $data, true); + + $this->output->set_object('taskcontent', $output); + } + + /** + * Resource information (form) action. + */ + public function action_info() + { + $id = $this->get_input('id', 'POST'); + $result = $this->api->get('resource.info', array('resource' => $id)); + $resource = $result->get(); + + //console("action_info()", $resource); + + $output = $this->resource_form(null, $resource); + + $this->output->set_object('taskcontent', $output); + } + + private function resource_form($attribs, $data = array()) + { + if (empty($attribs['id'])) { + $attribs['id'] = 'resource-form'; + } + + //console("resource_form(\$attribs, \$data)", $attribs, $data); + + // Form sections + $sections = array( + 'system' => 'resource.system', + 'other' => 'resource.other', + ); + + // field-to-section map and fields order + $fields_map = array( + 'type_id' => 'system', + 'type_id_name' => 'system', + + 'cn' => 'system', + 'ou' => 'system', + 'preferredlanguage' => 'system', + + 'mail' => 'system', + 'alias' => 'system', + 'mailalternateaddress' => 'system', + + 'member' => 'system', + 'uniquemember' => 'system', + 'memberurl' => 'system', + + 'nsrole' => 'system', + 'nsroledn' => 'system', + + /* Kolab Settings */ + 'kolabhomeserver' => 'system', + 'mailhost' => 'system', + 'mailquota' => 'system', + 'kolabfreebusyfuture' => 'system', + 'kolabinvitationpolicy' => 'system', + 'kolabdelegate' => 'system', + 'kolaballowsmtprecipient' => 'system', + 'kolaballowsmtpsender' => 'system', + ); + + // Prepare fields + list($fields, $types, $type) = $this->form_prepare('resource', $data); + + //console("Result from form_prepare", $fields, $types, $type); + + $add_mode = empty($data['id']); + $accttypes = array(); + + foreach ($types as $idx => $elem) { + $accttypes[$idx] = array('value' => $idx, 'content' => $elem['name']); + } + + // Add resource type id selector + $fields['type_id'] = array( + 'section' => 'system', + 'type' => kolab_form::INPUT_SELECT, + 'options' => $accttypes, + 'onchange' => "kadm.resource_save(true, 'system')", + ); + + //console($accttypes); + + // Hide account type selector if there's only one type + if (count($accttypes) < 2 || !$add_mode) { + //console("setting type_id form type to hidden"); + $fields['type_id']['type'] = kolab_form::INPUT_HIDDEN; + } + + // Create mode + if ($add_mode) { + // Page title + $title = $this->translate('resource.add'); + } + // Edit mode + else { + $title = $data['cn']; + + // Add resource type name + $fields['type_id_name'] = array( + 'label' => 'resource.type_id', + 'section' => 'system', + 'value' => $accttypes[$type]['content'], + ); + } + + // Create form object and populate with fields + $form = $this->form_create('resource', $attribs, $sections, $fields, $fields_map, $data, $add_mode); + + $form->set_title(kolab_html::escape($title)); + + $this->output->add_translation('resource.add.success', 'resource.edit.success', 'resource.delete.success'); + + return $form->output(); + } + + /** + * Users search form. + * + * @return string HTML output of the form + */ + public function search_form() + { + $form = new kolab_form(array('id' => 'search-form')); + + $form->add_section('criteria', kolab_html::escape($this->translate('search.criteria'))); + $form->add_element(array( + 'section' => 'criteria', + 'label' => $this->translate('search.field'), + 'name' => 'field', + 'type' => kolab_form::INPUT_SELECT, + 'options' => array( + 'cn' => kolab_html::escape($this->translate('search.name')), + 'email' => kolab_html::escape($this->translate('search.email')), + 'uid' => kolab_html::escape($this->translate('search.uid')), + ), + )); + $form->add_element(array( + 'section' => 'criteria', + 'label' => $this->translate('search.method'), + 'name' => 'method', + 'type' => kolab_form::INPUT_SELECT, + 'options' => array( + 'both' => kolab_html::escape($this->translate('search.contains')), + 'exact' => kolab_html::escape($this->translate('search.is')), + 'prefix' => kolab_html::escape($this->translate('search.prefix')), + ), + )); + + return $form->output(); + } + +} diff --git a/lib/client/kolab_client_task_role.php b/lib/client/kolab_client_task_role.php new file mode 100644 index 0000000..d012abd --- /dev/null +++ b/lib/client/kolab_client_task_role.php @@ -0,0 +1,339 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +class kolab_client_task_role extends kolab_client_task +{ + protected $ajax_only = true; + + protected $menu = array( + 'add' => 'role.add', + ); + + /** + * Default action. + */ + public function action_default() + { + $this->output->set_object('content', 'role', true); + $this->output->set_object('task_navigation', $this->menu()); + + $this->action_list(); + } + + /** + * Groups list action. + */ + public function action_list() + { + $page_size = 20; + $page = (int) self::get_input('page', 'POST'); + if (!$page || $page < 1) { + $page = 1; + } + + // request parameters + $post = array( + 'attributes' => array('cn'), +// 'sort_order' => 'ASC', + 'sort_by' => 'cn', + 'page_size' => $page_size, + 'page' => $page, + ); + + // search parameters + if (!empty($_POST['search'])) { + $search = self::get_input('search', 'POST', true); + $field = self::get_input('field', 'POST'); + $method = self::get_input('method', 'POST'); + + $search_request = array( + $field => array( + 'value' => $search, + 'type' => $method, + ), + ); + } + else if (!empty($_POST['search_request'])) { + $search_request = self::get_input('search_request', 'POST'); + $search_request = @unserialize(base64_decode($search_request)); + } + + if (!empty($search_request)) { + $post['search'] = $search_request; + $post['search_operator'] = 'OR'; + } + + // get roles list + $result = $this->api->post('roles.list', null, $post); + $count = (int) $result->get('count'); + $result = (array) $result->get('list'); + + // calculate records + if ($count) { + $start = 1 + max(0, $page - 1) * $page_size; + $end = min($start + $page_size - 1, $count); + } + + $rows = $head = $foot = array(); + $cols = array('name'); + $i = 0; + + // table header + $head[0]['cells'][] = array('class' => 'name', 'body' => $this->translate('role.list')); + + // table footer (navigation) + if ($count) { + $pages = ceil($count / $page_size); + $prev = max(0, $page - 1); + $next = $page < $pages ? $page + 1 : 0; + + $count_str = kolab_html::span(array( + 'content' => $this->translate('role.list.records', $start, $end, $count)), true); + $prev = kolab_html::a(array( + 'class' => 'prev' . ($prev ? '' : ' disabled'), + 'href' => '#', + 'onclick' => $prev ? "kadm.command('role.list', {page: $prev})" : "return false", + )); + $next = kolab_html::a(array( + 'class' => 'next' . ($next ? '' : ' disabled'), + 'href' => '#', + 'onclick' => $next ? "kadm.command('role.list', {page: $next})" : "return false", + )); + + $foot_body = kolab_html::span(array('content' => $prev . $count_str . $next)); + } + $foot[0]['cells'][] = array('class' => 'listnav', 'body' => $foot_body); + + // table body + if (!empty($result)) { + foreach ($result as $idx => $item) { + if (!is_array($item) || empty($item['cn'])) { + continue; + } + + $i++; + $cells = array(); + $cells[] = array('class' => 'name', 'body' => kolab_html::escape($item['cn']), + 'onclick' => "kadm.command('role.info', '$idx')"); + $rows[] = array('id' => $i, 'class' => 'selectable', 'cells' => $cells); + } + } + else { + $rows[] = array('cells' => array( + 0 => array('class' => 'empty-body', 'body' => $this->translate('role.norecords') + ))); + } + + $table = kolab_html::table(array( + 'id' => 'rolelist', + 'class' => 'list', + 'head' => $head, + 'body' => $rows, + 'foot' => $foot, + )); + + $this->output->set_env('search_request', $search_request ? base64_encode(serialize($search_request)) : null); + $this->output->set_env('list_page', $page); + $this->output->set_env('list_count', $count); + + $this->watermark('taskcontent'); + $this->output->set_object('rolelist', $table); + } + + /** + * Group information (form) action. + */ + public function action_info() + { + $id = $this->get_input('id', 'POST'); + $result = $this->api->get('role.info', array('role' => $id)); + $role = $result->get(); + $output = $this->role_form(null, $role); + + $this->output->set_object('taskcontent', $output); + } + + /** + * Groups adding (form) action. + */ + public function action_add() + { + $data = $this->get_input('data', 'POST'); + $output = $this->role_form(null, $data, true); + + $this->output->set_object('taskcontent', $output); + } + + /** + * Group edit/add form. + */ + private function role_form($attribs, $data = array()) + { + if (empty($attribs['id'])) { + $attribs['id'] = 'role-form'; + } + + // Form sections + $sections = array( + 'system' => 'role.system', + 'other' => 'role.other', + ); + + // field-to-section map and fields order + $fields_map = array( + 'type_id' => 'system', + 'type_id_name' => 'system', + 'cn' => 'system', + 'gidnumber' => 'system', + 'mail' => 'system', + 'member' => 'system', + 'uniquemember' => 'system', + 'memberurl' => 'system', + ); + + // Prepare fields + list($fields, $types, $type) = $this->form_prepare('role', $data); + + $add_mode = empty($data['id']); + $accttypes = array(); + + foreach ($types as $idx => $elem) { + $accttypes[$idx] = array('value' => $idx, 'content' => $elem['name']); + } + + // Add user type id selector + $fields['type_id'] = array( + 'section' => 'system', + 'type' => kolab_form::INPUT_SELECT, + 'options' => $accttypes, + 'onchange' => "kadm.role_save(true, 'system')", + ); + + // Hide account type selector if there's only one type + if (count($accttypes) < 2 || !$add_mode) { + $fields['type_id']['type'] = kolab_form::INPUT_HIDDEN; + } + + // Create mode + if ($add_mode) { + // Page title + $title = $this->translate('role.add'); + } + // Edit mode + else { + $title = $data['cn']; + + // Add user type name + $fields['type_id_name'] = array( + 'label' => 'role.type_id', + 'section' => 'system', + 'value' => $accttypes[$type]['content'], + ); + } + + // Create form object and populate with fields + $form = $this->form_create('role', $attribs, $sections, $fields, $fields_map, $data, $add_mode); + + $form->set_title(kolab_html::escape($title)); + + $this->output->add_translation('role.add.success', 'role.edit.success', 'role.delete.success'); + + return $form->output(); + } + + private function parse_members($list) + { + // convert to key=>value array, see kolab_api_service_form_value::list_options_uniquemember() + foreach ($list as $idx => $value) { + if (!empty($value['displayname'])) { + $list[$idx] = $value['displayname']; + } elseif (!empty($value['cn'])) { + $list[$idx] = $value['cn']; + } else { + //console("No display name or cn for $idx"); + } + + if (!empty($value['mail'])) { + $list[$idx] .= ' <' . $value['mail'] . '>'; + } + } + + return $list; + } + + /** + * Returns list of role types. + * + * @return array List of role types + */ + public function role_types() + { + if (!isset($_SESSION['role_types'])) { + $result = $this->api->post('role_types.list'); + $list = $result->get('list'); + + if (is_array($list)) { + $_SESSION['role_types'] = $list; + } + } + + return $_SESSION['role_types']; + } + + /** + * Users search form. + * + * @return string HTML output of the form + */ + public function search_form() + { + $form = new kolab_form(array('id' => 'search-form')); + + $form->add_section('criteria', kolab_html::escape($this->translate('search.criteria'))); + $form->add_element(array( + 'section' => 'criteria', + 'label' => $this->translate('search.field'), + 'name' => 'field', + 'type' => kolab_form::INPUT_SELECT, + 'options' => array( + 'cn' => kolab_html::escape($this->translate('search.name')), + 'mail' => kolab_html::escape($this->translate('search.email')), + ), + )); + $form->add_element(array( + 'section' => 'criteria', + 'label' => $this->translate('search.method'), + 'name' => 'method', + 'type' => kolab_form::INPUT_SELECT, + 'options' => array( + 'both' => kolab_html::escape($this->translate('search.contains')), + 'exact' => kolab_html::escape($this->translate('search.is')), + 'prefix' => kolab_html::escape($this->translate('search.prefix')), + ), + )); + + return $form->output(); + } + +} diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php index 5287006..2e81b65 100644 --- a/lib/client/kolab_client_task_user.php +++ b/lib/client/kolab_client_task_user.php @@ -197,6 +197,7 @@ class kolab_client_task_user extends kolab_client_task 'contact_info' => 'user.contact_info', 'system' => 'user.system', 'config' => 'user.config', + 'asterisk' => 'user.asterisk', 'other' => 'user.other', ); @@ -257,11 +258,28 @@ class kolab_client_task_user extends kolab_client_task 'kolabdelegate' => 'config', 'kolaballowsmtprecipient' => 'config', 'kolaballowsmtpsender' => 'config', + + /* Asterisk Settings */ + 'astaccountallowedcodec' => 'asterisk', + 'astaccountcallerid' => 'asterisk', + 'astaccountcontext' => 'asterisk', + 'astaccountdeny' => 'asterisk', + 'astaccounthost' => 'asterisk', + 'astaccountnat' => 'asterisk', + 'astaccountname' => 'asterisk', + 'astaccountqualify' => 'asterisk', + 'astaccountrealmedpassword' => 'asterisk', + 'astaccountsecret' => 'asterisk', + 'astaccounttype' => 'asterisk', + 'astcontext' => 'asterisk', + 'astextension' => 'asterisk', ); // Prepare fields list($fields, $types, $type) = $this->form_prepare('user', $data, array('userpassword2')); + //console("Result from form_prepare", $fields, $types, $type); + $add_mode = empty($data['id']); $accttypes = array(); diff --git a/lib/functions.php b/lib/functions.php index 441752f..9761358 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -108,23 +108,26 @@ function write_log($name, $line) $logfile = $log_dir . '/' . $name; $date = date('d-M-Y H:i:s O'); $sess_id = session_id(); - $line = sprintf("[%s]%s: %s\n", $date, $sess_id ? "($sess_id)" : '', $line); + $logline = sprintf("[%s]%s: %s\n", $date, $sess_id ? "($sess_id)" : '', $line); if ($fp = @fopen($logfile, 'a')) { - fwrite($fp, $line); + fwrite($fp, $logline); fflush($fp); fclose($fp); - return true; + return; } - return false; + if ($name == 'errors') { + // send error to PHPs error handler if write to file didn't succeed + trigger_error($line, E_USER_ERROR); + } } function timer($time = null, $label = '') { $now = microtime(true); if ($time) { - console(($label ? $label.' ' : '') . sprintf('%.4f', $now - $time)); + //console(($label ? $label.' ' : '') . sprintf('%.4f', $now - $time)); } return $now; } diff --git a/lib/kolab_api_controller.php b/lib/kolab_api_controller.php index 7831971..d96d4ac 100644 --- a/lib/kolab_api_controller.php +++ b/lib/kolab_api_controller.php @@ -48,23 +48,27 @@ class kolab_api_controller ); } else { - throw new Exception("Unknown method", 400); + throw new Exception("Unknown method " . $_GET['method'], 400); } } else { - throw new Exception("Unknown service", 400); + throw new Exception("Unknown service " . $_GET['service'], 400); } // TODO: register services based on config or whatsoever - $this->add_service('form_value', 'kolab_api_service_form_value'); - $this->add_service('group_types', 'kolab_api_service_group_types'); - $this->add_service('group', 'kolab_api_service_group'); - $this->add_service('groups', 'kolab_api_service_groups'); - $this->add_service('user_types', 'kolab_api_service_user_types'); - $this->add_service('user', 'kolab_api_service_user'); - $this->add_service('users', 'kolab_api_service_users'); - $this->add_service('domains', 'kolab_api_service_domains'); - $this->add_service('roles', 'kolab_api_service_roles'); + $this->add_service('domain', 'kolab_api_service_domain'); + $this->add_service('domains', 'kolab_api_service_domains'); + $this->add_service('form_value', 'kolab_api_service_form_value'); + $this->add_service('group_types', 'kolab_api_service_group_types'); + $this->add_service('group', 'kolab_api_service_group'); + $this->add_service('groups', 'kolab_api_service_groups'); + $this->add_service('resource_types', 'kolab_api_service_resource_types'); + $this->add_service('resource', 'kolab_api_service_resource'); + $this->add_service('resources', 'kolab_api_service_resources'); + $this->add_service('roles', 'kolab_api_service_roles'); + $this->add_service('user_types', 'kolab_api_service_user_types'); + $this->add_service('user', 'kolab_api_service_user'); + $this->add_service('users', 'kolab_api_service_users'); } /** @@ -73,7 +77,7 @@ class kolab_api_controller public function add_service($service, $handler) { if ($this->services[$service]) { - error_log("Service $service is already registered."); + //console("Service $service is already registered."); return false; } @@ -100,7 +104,7 @@ class kolab_api_controller } } - error_log("Unknown service $service"); + //console("Unknown service $service"); throw new Exception("Unknown service", 400); } @@ -132,7 +136,7 @@ class kolab_api_controller $method = $this->request['method']; $postdata = @json_decode($postdata, true); - console("Calling method " . $method . " on service " . $service); + //console("Calling method " . $method . " on service " . $service); // validate user session if ($service != 'system' || $method != 'authenticate') { @@ -180,7 +184,7 @@ class kolab_api_controller $method = $this->request['method']; $url = rtrim($url, '/') . '/' . $service . '.' . $method; - console("Proxying " . $url); + //console("Proxying " . $url); $request = new HTTP_Request2(); $url = new Net_URL2($url); @@ -296,7 +300,9 @@ class kolab_api_controller */ private function capabilities() { + //console("system.capabilities called"); $auth = Auth::get_instance(); + $this->domains = $auth->list_domains(); $result = array(); @@ -304,14 +310,17 @@ class kolab_api_controller // Should we have no permissions to list domain name spaces, // we should always return our own. if (count($this->domains) < 1) { + //console("As there is but one domain, we insert our own"); $this->domains[] = $_SESSION['user']->get_domain(); } + //console("\$this->domains:", $this->domains); + // add capabilities of all registered services foreach ($this->domains as $domain) { // TODO: 'associateddomain' is very specific to 389ds based deployments, and this // is supposed to be very generic. - $domain_name = is_array($domain) ? $domain['associateddomain'] : $domain; + $domain_name = is_array($domain) ? (is_array($domain['associateddomain']) ? $domain['associateddomain'][0] : $domain['associateddomain']) : $domain; // define our very own capabilities $actions = array( 'system.quit' => array('type' => 'w'), @@ -325,6 +334,8 @@ class kolab_api_controller } } + //console("api capabilities", $domain, $domain_name); + $result[$domain_name] = array('actions' => $actions); } diff --git a/lib/kolab_api_service.php b/lib/kolab_api_service.php index d43aa67..7fca044 100644 --- a/lib/kolab_api_service.php +++ b/lib/kolab_api_service.php @@ -59,7 +59,7 @@ abstract class kolab_api_service */ protected function object_type_attributes($object_name, $type_id, $required = true) { - $supported = array('group', 'user'); + $supported = array('domain', 'group', 'resource', 'role', 'user'); if (!$object_name || !in_array($object_name, $supported)) { return array(); } @@ -75,7 +75,29 @@ abstract class kolab_api_service $object_types = $this->object_types($object_name); if (empty($object_types[$type_id])) { - throw new Exception($this->controller->translate($object_name . '.invalidtypeid'), 35); + if ($object_name == 'domain') { + $result = array( + 'auto_form_fields' => array(), + 'form_fields' => array( + 'associateddomain' => array( + 'type' => 'list' + ), + ), + 'fields' => array( + 'objectclass' => array( + 'top', + 'domainrelatedobject', + ), + ), + ); + + //console("object_type_attributes('domain', $type_id);", $result); + + return $result; + + } else { + throw new Exception($this->controller->translate($object_name . '.invalidtypeid'), 35); + } } return $object_types[$type_id]['attributes']; @@ -91,6 +113,8 @@ abstract class kolab_api_service */ protected function object_type_id($object_name, $object_class) { + if ($object_name == 'domain') return 1; + if (empty($object_class)) { return null; } @@ -119,8 +143,8 @@ abstract class kolab_api_service $commonalities = count($object_class) - $differences; $elem_score = $differences > 0 ? ($commonalities / $differences) : $commonalities; -// console("\$object_class not in \$ref_class (" . $elem['key'] . "): " . implode(", ", $_object_class)); -// console("\$ref_class not in \$object_class (" . $elem['key'] . "): " . implode(", ", $_ref_class)); + //console("\$object_class not in \$ref_class (" . $elem['key'] . "): " . implode(", ", $_object_class)); + //console("\$ref_class not in \$object_class (" . $elem['key'] . "): " . implode(", ", $_ref_class)); //console("Score for $object_name type " . $elem['name'] . ": " . $elem_score . "(" . $commonalities . "/" . $differences . ")"); if ($elem_score > $type_score) { @@ -141,11 +165,12 @@ abstract class kolab_api_service */ protected function object_types($object_name) { - $supported = array('group', 'user'); + $supported = array('group', 'resource', 'user'); if (!$object_name || !in_array($object_name, $supported)) { return array(); } + if (!empty($this->cache['object_types']) && !empty($this->cache['object_types'][$object_name])) { return $this->cache['object_types'][$object_name]; } @@ -174,70 +199,12 @@ abstract class kolab_api_service } } - return $this->cache['object_types'][$object_name] = $object_types; - } - - /** - * Parses result attributes - * - * @param string $object_name Name of the object (user, group, etc.) - * @param array $attrs Entry attributes - * - * @return array Entry attributes - */ - protected function parse_result_attributes($object_name, $attrs = array()) - { - if (empty($attrs) || !is_array($attrs)) { - return $attrs; - } - - $conf = Conf::get_instance(); - $auth = Auth::get_instance(); - $dn = key($attrs); - $attrs = $attrs[$dn]; - $extra_attrs = array(); + //console("Object types for " . $object_name, $object_types); - // add group type id to the result - $attrs['type_id'] = $this->object_type_id($object_name, $attrs['objectclass']); - - // Search for attributes associated with the type_id that are not part - // of the results returned earlier. Example: nsrole / nsroledn / aci, etc. - // @TODO: this should go to LDAP class - if ($attrs['type_id']) { - $uta = $this->object_type_attributes($object_name, $attrs['type_id']); +// return $object_types; - foreach ((array)$uta as $field_type => $attributes) { - foreach ($attributes as $attribute => $data) { - if (!array_key_exists($attribute, $attrs)) { - $extra_attrs[] = $attribute; - } - } - } - } - - // Insert the persistent, unique attribute - $unique_attr = $conf->get('unique_attribute'); - if (!$unique_attr) { - $unique_attr = 'nsuniqueid'; - } - - if (!array_key_exists($unique_attr, $attrs)) { - $extra_attrs[] = $unique_attr; - } - - // Get extra attributes - if (!empty($extra_attrs)) { - $extra_attrs = $auth->get_attributes($dn, $extra_attrs); - if (!empty($extra_attrs)) { - $attrs = array_merge($attrs, $extra_attrs); - } - } - - // Replace unique attribute with 'id' key - $attrs['id'] = $attrs[$unique_attr]; - unset($attrs[$unique_attr]); + return $this->cache['object_types'][$object_name] = $object_types; - return $attrs; } /** @@ -253,8 +220,13 @@ abstract class kolab_api_service $type_attrs = $this->object_type_attributes($object_name, $attribs['type_id']); //console("parse_input_attributes", $type_attrs); + //console("called with \$attribs", $attribs); $form_service = $this->controller->get_service('form_value'); + + // With the result, start validating the input + $form_service->validate(null, $attribs); + $result = array(); if (isset($type_attrs['form_fields'])) { @@ -302,4 +274,75 @@ abstract class kolab_api_service return $result; } + /** + * Parses result attributes + * + * @param string $object_name Name of the object (user, group, etc.) + * @param array $attrs Entry attributes + * + * @return array Entry attributes + */ + protected function parse_result_attributes($object_name, $attrs = array()) + { + //console("parse_result_attributes($object_name, \$attrs = ", $attrs); + + if (empty($attrs) || !is_array($attrs)) { + return $attrs; + } + + $conf = Conf::get_instance(); + $auth = Auth::get_instance(); + $dn = key($attrs); + $attrs = $attrs[$dn]; + $extra_attrs = array(); + + // add group type id to the result + $attrs['type_id'] = $this->object_type_id($object_name, $attrs['objectclass']); + + if (empty($attrs['type_id'])) { + if ($object_name == 'domain') { + $attrs['type_id'] = 1; + } + } + + // Search for attributes associated with the type_id that are not part + // of the results returned earlier. Example: nsrole / nsroledn / aci, etc. + // @TODO: this should go to LDAP class + if ($attrs['type_id']) { + $uta = $this->object_type_attributes($object_name, $attrs['type_id']); + + foreach ((array)$uta as $field_type => $attributes) { + foreach ($attributes as $attribute => $data) { + if (!array_key_exists($attribute, $attrs)) { + $extra_attrs[] = $attribute; + } + } + } + } + + // Insert the persistent, unique attribute + $unique_attr = $conf->get('unique_attribute'); + if (!$unique_attr) { + $unique_attr = 'nsuniqueid'; + } + + if (!array_key_exists($unique_attr, $attrs)) { + $extra_attrs[] = $unique_attr; + } + + // Get extra attributes + if (!empty($extra_attrs)) { + $extra_attrs = $auth->get_attributes($dn, $extra_attrs); + if (!empty($extra_attrs)) { + $attrs = array_merge($attrs, $extra_attrs); + } + } + + // Replace unique attribute with 'id' key + $attrs['id'] = $attrs[$unique_attr]; + unset($attrs[$unique_attr]); + + return $attrs; + } + } diff --git a/lib/kolab_client_api.php b/lib/kolab_client_api.php index b8d101a..359fe73 100644 --- a/lib/kolab_client_api.php +++ b/lib/kolab_client_api.php @@ -60,6 +60,28 @@ class kolab_client_api public function init() { $this->request = new HTTP_Request2(); + + // Configure connection options + $config = Conf::get_instance(); + $options = array( + 'ssl_verify_peer' => Conf::BOOL, + 'ssl_verify_host' => Conf::BOOL, + 'ssl_cafile' => Conf::STRING, + 'ssl_capath' => Conf::STRING, + 'ssl_local_cert' => Conf::STRING, + 'ssl_passphrase' => Conf::STRING, + ); + + foreach ($options as $optname => $opttype) { + if (($optvalue = $config->get('kolab_wap', $optname, $opttype)) !== null) { + try { + $this->request->setConfig($optname, $optvalue); + } + catch (Exception $e) { + write_log('errors', $e->getMessage()); + } + } + } } /** @@ -128,6 +150,8 @@ class kolab_client_api $this->request->setMethod(HTTP_Request2::METHOD_GET); + //console("GET", $url); + return $this->get_response($url); } @@ -147,6 +171,8 @@ class kolab_client_api $this->request->setMethod(HTTP_Request2::METHOD_POST); $this->request->setBody(@json_encode($post)); + //console("POST", $url, $post); + return $this->get_response($url); } diff --git a/lib/kolab_client_output.php b/lib/kolab_client_output.php index 6454acf..d40c323 100644 --- a/lib/kolab_client_output.php +++ b/lib/kolab_client_output.php @@ -51,7 +51,20 @@ class kolab_client_output */ private function init() { - require_once 'Smarty/Smarty.class.php'; + $conf = Conf::get_instance(); + + $smarty_path = array('Smarty', 'smarty3', 'smarty'); + + if ($path = $conf->get('kolab_wap', 'smarty_path')) { + array_unshift($smarty_path, $path); + } + + foreach ($smarty_path as $path) { + @include_once "$path/Smarty.class.php"; + if (class_exists('Smarty', false)) { + break; + } + } $SMARTY = new Smarty; diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php index ab5b191..1740384 100644 --- a/lib/kolab_client_task.php +++ b/lib/kolab_client_task.php @@ -351,10 +351,7 @@ class kolab_client_task $msg . (isset($args['file']) ? sprintf(' in %s on line %d', $args['file'], $args['line']) : ''), $_SERVER['REQUEST_METHOD']); - if (!write_log('errors', $log_line)) { - // send error to PHPs error handler if write_log() didn't succeed - trigger_error($msg, E_USER_ERROR); - } + write_log('errors', $log_line); if (!$output) { return; @@ -483,7 +480,7 @@ class kolab_client_task $capabilities = $this->capabilities(); - //console($capabilities); + //console("Capabilities", $capabilities); foreach ($this->menu as $idx => $label) { //console("$task: $task, idx: $idx, label: $label"); @@ -524,6 +521,31 @@ class kolab_client_task } /** + * Returns list of resource types. + * + * @return array List of resource types + */ + protected function resource_types() + { + + if (isset($_SESSION['resource_types'])) { + return $_SESSION['resource_types']; + } + + + $result = $this->api->post('resource_types.list'); + $list = $result->get('list'); + + + if (is_array($list) && !$this->config_get('devel_mode')) { + $_SESSION['resource_types'] = $list; + } + + return $list; + } + + + /** * Returns list of user types. * * @return array List of user types @@ -541,6 +563,8 @@ class kolab_client_task $_SESSION['user_types'] = $list; } + //console("user_types() \$list", $list); + return $list; } @@ -583,6 +607,8 @@ class kolab_client_task $result = $this->api->post('system.capabilities'); $list = $result->get('list'); + //console("Capabilities obtained from the API", $list); + if (is_array($list)) { $_SESSION['capabilities'] = $list; } @@ -766,7 +792,12 @@ class kolab_client_task */ protected function form_prepare($name, &$data, $extra_fields = array()) { + //console("Preparing form for $name with data", $data); + $types = (array) $this->{$name . '_types'}(); + + //console("form_prepare types", $types); + $form_id = $attribs['id']; $add_mode = empty($data['id']); @@ -963,6 +994,7 @@ class kolab_client_task $data[$fname] = (array) $data[$fname]; } + //console("The data for field $fname at this point is", $data[$fname]); // request parameters $post = array( 'list' => $data[$fname], @@ -976,6 +1008,7 @@ class kolab_client_task $result = $result->get('list'); $data[$fname] = $result; + //console("Set \$data['$fname'] to", $result); } } @@ -1005,13 +1038,24 @@ class kolab_client_task */ protected function form_create($name, $attribs, $sections, $fields, $fields_map, $data, $add_mode) { + //console("Creating form for $name with data", $data); + + //console("Assign fields to sections", $fields); // Assign sections to fields foreach ($fields as $idx => $field) { if (!$field['section']) { $fields[$idx]['section'] = isset($fields_map[$idx]) ? $fields_map[$idx] : 'other'; + //console("Assigned field $idx to section " . $fields[$idx]['section']); +/* + } else { + $fields[$idx]['section'] = 'other'; + //console("Assigned field $idx to section " . $fields[$idx]['section']); +*/ } } + //console("Using fields_map", $fields_map); + // Sort foreach ($fields_map as $idx => $val) { if (array_key_exists($idx, $fields)) { @@ -1026,13 +1070,19 @@ class kolab_client_task $fields_map = array_merge($fields_map, $fields); } + //console("Using attribs", $attribs); + $form = new kolab_form($attribs); $assoc_fields = array(); $req_fields = array(); $writeable = 0; $auto_fields = $this->output->get_env('auto_fields'); - //console("\$auto_fields", $auto_fields); + + //console("form_create() \$attribs", $attribs); + //console("form_create() \$auto_fields", $auto_fields); + + //console("Going to walk through sections", $sections); // Parse elements and add them to the form object foreach ($sections as $section_idx => $section) { @@ -1052,6 +1102,9 @@ class kolab_client_task $field['section'] = $section_idx; if (empty($field['value']) && !empty($data[$idx])) { + + //console("Using data value", $data[$idx], "for value of field $idx"); + $field['value'] = $data[$idx]; // Convert data for the list field with autocompletion @@ -1116,6 +1169,8 @@ class kolab_client_task } } + //console("Adding field to form", $field); + $form->add_element($field); } } diff --git a/lib/kolab_recipient_policy.php b/lib/kolab_recipient_policy.php index 492df0b..10179d8 100644 --- a/lib/kolab_recipient_policy.php +++ b/lib/kolab_recipient_policy.php @@ -44,6 +44,12 @@ class kolab_recipient_policy { foreach ($groupdata as $key => $value) { if (isset($groupdata['preferredlanguage'])) { setlocale(LC_ALL, $groupdata['preferredlanguage']); + } else { + $conf = Conf::get_instance(); + $locale = $conf->get('default_locale'); + if (!empty($locale)) { + setlocale(LC_ALL, $locale); + } } if (!is_array($groupdata[$key])) { @@ -73,6 +79,12 @@ class kolab_recipient_policy { if (isset($userdata['preferredlanguage'])) { setlocale(LC_ALL, $userdata['preferredlanguage']); + } else { + $conf = Conf::get_instance(); + $locale = $conf->get('default_locale'); + if (!empty($locale)) { + setlocale(LC_ALL, $locale); + } } if (!is_array($userdata[$_key])) { @@ -157,7 +169,7 @@ class kolab_recipient_policy { ); } } else { - console("Key " . $substrings[1][$x] . " does not exist in \$userdata"); + //console("Key " . $substrings[1][$x] . " does not exist in \$userdata"); } } @@ -175,6 +187,9 @@ class kolab_recipient_policy { ); $userdata = self::normalize_userdata($userdata); + if (!array_key_exists('mail', $userdata)) { + $userdata['mail'] = self::primary_mail($userdata); + } $conf = Conf::get_instance(); @@ -206,7 +221,7 @@ class kolab_recipient_policy { if (array_key_exists($substrings[1][$x], $userdata)) { $userdata[$substrings[1][$x]] = substr($userdata[$substrings[1][$x]], $substrings[2][$x], $substrings[3][$x]); } else { - console("Key " . $substrings[1][$x] . " does not exist in \$userdata"); + //console("Key " . $substrings[1][$x] . " does not exist in \$userdata"); } $rule = preg_replace( @@ -234,6 +249,13 @@ class kolab_recipient_policy { eval("\$result = sprintf('" . $format . "', '" . implode("', '", array_values($result)) . "');"); if ($result = self::parse_email($result)) { + // See if the equivalent is already in the 'mail' attribute value(s) + if (!empty($userdata['mail'])) { + if (strtolower($userdata['mail']) == strtolower($result)) { + continue; + } + } + $secondary_mail_addresses[] = $result; } } diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php index 07e35bb..564803b 100644 --- a/lib/locale/en_US.php +++ b/lib/locale/en_US.php @@ -1,25 +1,85 @@ <?php -$LANG['loading'] = 'Loading...'; -$LANG['saving'] = 'Saving data...'; +$LANG['about.community'] = 'This is the Community Edition of the <b>Kolab Server</b>.'; +$LANG['about.warranty'] = 'It comes with absolutely <b>no warranties</b> and is typically run entirely self supported. You can find help & information on the community <a href="http://kolab.org">web site</a> & <a href="http://wiki.kolab.org">wiki</a>.'; +$LANG['about.support'] = 'Professional support is available from <a href="http://kolabsys.com">Kolab Systems</a>.'; +$LANG['creatorsname'] = 'Created by'; +$LANG['days'] = 'days'; +$LANG['debug'] = 'Debug info'; +$LANG['delete.button'] = 'Delete'; $LANG['deleting'] = 'Deleting data...'; + +$LANG['domain.add'] = 'Add Domain'; +$LANG['domain.add.success'] = 'Added domain'; +$LANG['domain.associateddomain'] = 'Domain name(s)'; +$LANG['domain.edit'] = 'Edit domain'; +$LANG['domain.edit.success'] = 'Domain updated'; +$LANG['domain.inetdomainbasedn'] = 'Custom Root DN(s)'; +$LANG['domain.list'] = 'Domains List'; +$LANG['domain.list.records'] = '$1 to $2 of $3'; +$LANG['domain.o'] = 'Organization'; +$LANG['domain.other'] = 'Other'; +$LANG['domain.system'] = 'System'; +$LANG['domain.type_id'] = 'Standard Domain'; + $LANG['error'] = 'Error'; -$LANG['servererror'] = 'Server Error!'; -$LANG['loginerror'] = 'Incorrect username or password!'; -$LANG['internalerror'] = 'Internal system error!'; -$LANG['welcome'] = 'Welcome to the Kolab Groupware Server Maintenance'; -$LANG['reqtime'] = 'Request time: $1 sec.'; -$LANG['debug'] = 'Debug info'; -$LANG['info'] = 'Information'; -$LANG['creatorsname'] = 'Created by'; -$LANG['modifiersname'] = 'Modified by'; -$LANG['session.expired'] = 'Session has expired. Login again, please'; +$LANG['form.required.empty'] = 'Some of the required fields are empty!'; + +$LANG['group.add'] = 'Add Group'; +$LANG['group.add.success'] = 'Group created successfully.'; +$LANG['group.cn'] = 'Common name'; +$LANG['group.delete.success'] = 'Group deleted successfully.'; +$LANG['group.edit.success'] = 'Group edited successfully.'; +$LANG['group.gidnumber'] = 'Primary group number'; +$LANG['group.list'] = 'Groups List'; +$LANG['group.list.records'] = '$1 to $2 of $3'; +$LANG['group.mail'] = 'Primary Email Address'; +$LANG['group.member'] = 'Member(s)'; +$LANG['group.norecords'] = 'No group records found!'; +$LANG['group.other'] = 'Other'; +$LANG['group.system'] = 'System'; +$LANG['group.type_id'] = 'Group type'; +$LANG['group.uniquemember'] = 'Members'; + +$LANG['info'] = 'Information'; +$LANG['internalerror'] = 'Internal system error!'; +$LANG['loading'] = 'Loading...'; $LANG['login.username'] = 'Username:'; $LANG['login.password'] = 'Password:'; $LANG['login.login'] = 'Login'; +$LANG['loginerror'] = 'Incorrect username or password!'; +$LANG['MB'] = 'MB'; -$LANG['form.required.empty'] = 'Some of the required fields are empty!'; +$LANG['menu.about'] = 'About'; +$LANG['menu.domains'] = 'Domains'; +$LANG['menu.groups'] = 'Groups'; +$LANG['menu.kolab'] = 'Kolab'; +$LANG['menu.kolabsys'] = 'Kolab Systems'; +$LANG['menu.resources'] = 'Resources'; +$LANG['menu.technology'] = 'Technology'; +$LANG['menu.users'] = 'Users'; + +$LANG['modifiersname'] = 'Modified by'; +$LANG['password.generate'] = 'Generate password'; +$LANG['reqtime'] = 'Request time: $1 sec.'; + +$LANG['resource.add'] = 'Add Resource'; +$LANG['resource.add.success'] = 'Added Resource'; +$LANG['resource.cn'] = 'Name'; +$LANG['resource.delete'] = 'Delete Resource'; +$LANG['resource.delete.success'] = 'Successfully deleted Resource'; +$LANG['resource.edit'] = 'Edit Resource'; +$LANG['resource.edit.success'] = 'Successfully updated Resource'; +$LANG['resource.kolabtargetfolder'] = 'Target Folder'; +$LANG['resource.list.records'] = '$1 to $2 of $3'; +$LANG['resource.mail'] = 'Mail Address'; +$LANG['resource.other'] = 'Other'; +$LANG['resource.system'] = 'System'; +$LANG['resource.type_id'] = 'Resource Type'; +$LANG['resource.uniquemember'] = 'Collection Members'; + +$LANG['saving'] = 'Saving data...'; $LANG['search'] = 'Search'; $LANG['search.criteria'] = 'Search criteria'; @@ -35,106 +95,91 @@ $LANG['search.uid'] = 'UID'; $LANG['search.loading'] = 'Searching...'; $LANG['search.acchars'] = 'At least $min characters required for autocompletion'; -$LANG['menu.users'] = 'Users'; -$LANG['menu.groups'] = 'Groups'; -$LANG['menu.about'] = 'About'; -$LANG['menu.kolab'] = 'Kolab'; -$LANG['menu.kolabsys'] = 'Kolab Systems'; -$LANG['menu.technology'] = 'Technology'; +$LANG['servererror'] = 'Server Error!'; + +$LANG['session.expired'] = 'Session has expired. Login again, please'; + +$LANG['submit.button'] = 'Submit'; $LANG['user.add'] = 'Add User'; +$LANG['user.add.success'] = 'User created successfully.'; +$LANG['user.alias'] = 'Secondary Email Address(es)'; +$LANG['user.astaccountallowedcodec'] = 'Allowed codec(s)'; +$LANG['user.astaccountcallerid'] = 'Caller ID'; +$LANG['user.astaccountcontext'] = 'Account Context'; +$LANG['user.astaccountdeny'] = 'Account deny'; +$LANG['user.astaccounthost'] = 'Asterisk Host'; +$LANG['user.astaccountnat'] = 'Account uses NAT'; +$LANG['user.astaccountname'] = 'Asterisk Account Name'; +$LANG['user.astaccountqualify'] = 'Account Qualify'; +$LANG['user.astaccountrealmedpassword'] = 'Realmed Account Password'; +$LANG['user.astaccountsecret'] = 'Plaintext Password'; +$LANG['user.astaccounttype'] = 'Account Type'; +$LANG['user.astcontext'] = 'Asterisk Context'; +$LANG['user.asterisk'] = 'Asterisk SIP'; +$LANG['user.astextension'] = 'Asterisk Extension'; $LANG['user.c'] = 'Country'; +$LANG['user.city'] = 'City'; $LANG['user.cn'] = 'Common name'; $LANG['user.config'] = 'Configuration'; $LANG['user.contact'] = 'Contact'; $LANG['user.contact_info'] = 'Contact Information'; +$LANG['user.country'] = 'Country'; +$LANG['user.country.desc'] = '2 letter code from ISO 3166-1'; +$LANG['user.delete.success'] = 'User deleted successfully.'; +$LANG['user.displayname'] = 'Display name'; +$LANG['user.edit.success'] = 'User edited successfully.'; +$LANG['user.fax'] = 'Fax number'; +$LANG['user.fbinterval'] = 'Free-Busy interval'; +$LANG['user.fbinterval.desc'] = 'Leave blank for default (60 days)'; +$LANG['user.gidnumber'] = 'Primary group number'; +$LANG['user.givenname'] = 'Given name'; +$LANG['user.homedirectory'] = 'Home directory'; $LANG['user.homephone'] = 'Home Phone Number'; +$LANG['user.initials'] = 'Initials'; +$LANG['user.invitation-policy'] = 'Invitation policy'; $LANG['user.kolaballowsmtprecipient'] = 'Recipient(s) Access List'; $LANG['user.kolaballowsmtpsender'] = 'Sender Access List'; $LANG['user.kolabdelegate'] = 'Delegates'; +$LANG['user.kolabhomeserver'] = 'Email Server'; $LANG['user.kolabinvitationpolicy'] = 'Invitation Handling Policy'; $LANG['user.l'] = 'City, Region'; $LANG['user.list'] = 'Users List'; $LANG['user.list.records'] = '$1 to $2 of $3'; +$LANG['user.loginshell'] = 'Shell'; +$LANG['user.mail'] = 'Primary Email Address'; +$LANG['user.mailalternateaddress'] = 'Secondary Email Address(es)'; +$LANG['user.mailhost'] = 'Email Server'; $LANG['user.mailquota'] = 'Quota'; $LANG['user.mailquota.desc'] = 'Leave blank for unlimited'; $LANG['user.mobile'] = 'Mobile Phone Number'; $LANG['user.name'] = 'Name'; $LANG['user.norecords'] = 'No user records found!'; +$LANG['user.nsrole'] = 'Role(s)'; +$LANG['user.nsroledn'] = $LANG['user.nsrole']; $LANG['user.other'] = 'Other'; $LANG['user.o'] = 'Organization'; +$LANG['user.org'] = 'Organization'; +$LANG['user.orgunit'] = 'Organizational Unit'; $LANG['user.ou'] = 'Organizational Unit'; $LANG['user.pager'] = 'Pager Number'; +$LANG['user.password.mismatch'] = 'Passwords do not match!'; $LANG['user.personal'] = 'Personal'; +$LANG['user.phone'] = 'Phone number'; $LANG['user.postalcode'] = 'Postal Code'; +$LANG['user.postbox'] = 'Postal box'; +$LANG['user.postcode'] = 'Postal code'; +$LANG['user.preferredlanguage'] = 'Native tongue'; +$LANG['user.room'] = 'Room number'; $LANG['user.sn'] = 'Surname'; +$LANG['user.street'] = 'Street'; $LANG['user.system'] = 'System'; $LANG['user.telephonenumber'] = 'Phone Number'; $LANG['user.title'] = 'Job Title'; -$LANG['user.givenname'] = 'Given name'; -$LANG['user.displayname'] = 'Display name'; -$LANG['user.mail'] = 'Primary Email Address'; -$LANG['user.mailhost'] = 'Email Server'; -$LANG['user.kolabhomeserver'] = 'Email Server'; -$LANG['user.initials'] = 'Initials'; -$LANG['user.country'] = 'Country'; -$LANG['user.country.desc'] = '2 letter code from ISO 3166-1'; -$LANG['user.phone'] = 'Phone number'; -$LANG['user.fax'] = 'Fax number'; -$LANG['user.room'] = 'Room number'; -$LANG['user.street'] = 'Street'; -$LANG['user.city'] = 'City'; -$LANG['user.postbox'] = 'Postal box'; -$LANG['user.postcode'] = 'Postal code'; -$LANG['user.org'] = 'Organization'; -$LANG['user.orgunit'] = 'Organizational Unit'; -$LANG['user.fbinterval'] = 'Free-Busy interval'; -$LANG['user.fbinterval.desc'] = 'Leave blank for default (60 days)'; $LANG['user.type_id'] = 'Account type'; -$LANG['user.alias'] = 'Secondary Email Address(es)'; -$LANG['user.mailalternateaddress'] = 'Secondary Email Address(es)'; -$LANG['user.invitation-policy'] = 'Invitation policy'; -$LANG['user.delegate'] = 'Email delegates'; -$LANG['user.delegate.desc'] = 'Others allowed to send emails with a "From" address of this account'; -$LANG['user.smtp-recipients'] = 'Allowed recipients'; -$LANG['user.smtp-recipients.desc'] = 'Restricts allowed recipients of SMTP messages'; $LANG['user.uid'] = 'Unique identity (UID)'; -$LANG['user.nsrole'] = 'Role(s)'; -$LANG['user.nsroledn'] = $LANG['user.nsrole']; $LANG['user.userpassword'] = 'Password'; $LANG['user.userpassword2'] = 'Confirm password'; -$LANG['user.password.mismatch'] = 'Passwords do not match!'; -$LANG['user.homeserver'] = 'Mailbox home server'; -$LANG['user.add.success'] = 'User created successfully.'; -$LANG['user.delete.success'] = 'User deleted successfully.'; -$LANG['user.edit.success'] = 'User edited successfully.'; -$LANG['user.preferredlanguage'] = 'Native tongue'; -$LANG['user.gidnumber'] = 'Primary group number'; -$LANG['user.homedirectory'] = 'Home directory'; -$LANG['user.loginshell'] = 'Shell'; $LANG['user.uidnumber'] = 'User ID number'; -$LANG['group.add'] = 'Add Group'; -$LANG['group.member'] = 'Member(s)'; -$LANG['group.norecords'] = 'No group records found!'; -$LANG['group.list'] = 'Groups List'; -$LANG['group.list.records'] = '$1 to $2 of $3'; -$LANG['group.cn'] = 'Common name'; -$LANG['group.mail'] = 'Primary Email Address'; -$LANG['group.type_id'] = 'Group type'; -$LANG['group.add.success'] = 'Group created successfully.'; -$LANG['group.delete.success'] = 'Group deleted successfully.'; -$LANG['group.edit.success'] = 'Group edited successfully.'; -$LANG['group.gidnumber'] = 'Primary group number'; -$LANG['group.uniquemember'] = 'Members'; -$LANG['group.system'] = 'System'; -$LANG['group.other'] = 'Other'; - -$LANG['MB'] = 'MB'; -$LANG['days'] = 'days'; -$LANG['submit.button'] = 'Submit'; -$LANG['delete.button'] = 'Delete'; -$LANG['about.community'] = 'This is the Community Edition of the <b>Kolab Server</b>.'; -$LANG['about.warranty'] = 'It comes with absolutely <b>no warranties</b> and is typically run entirely self supported. You can find help & information on the community <a href="http://kolab.org">web site</a> & <a href="http://wiki.kolab.org">wiki</a>.'; -$LANG['about.support'] = 'Professional support is available from <a href="http://kolabsys.com">Kolab Systems</a>.'; -$LANG['password.generate'] = 'Generate password'; +$LANG['welcome'] = 'Welcome to the Kolab Groupware Server Maintenance'; diff --git a/public_html/api/index.php b/public_html/api/index.php index cecb2f3..b5be580 100644 --- a/public_html/api/index.php +++ b/public_html/api/index.php @@ -32,7 +32,7 @@ try { $postdata = $_SERVER['REQUEST_METHOD'] == 'POST' ? file_get_contents('php://input') : null; $controller->dispatch($postdata); } catch(Exception $e) { - error_log('API Error: ' . $e->getMessage()); + //console('API Error: ' . $e->getMessage()); $controller->output->error($e->getMessage(), $e->getCode()); } diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js index bc83cac..b640679 100644 --- a/public_html/js/kolab_admin.js +++ b/public_html/js/kolab_admin.js @@ -1038,6 +1038,51 @@ function kolab_admin() return false; }; + this.domain_info = function(id) + { + this.http_post('domain.info', {id: id}); + }; + + this.domain_save = function(reload, section) + { + var data = this.serialize_form('#'+this.env.form_id), + action = data.id ? 'edit' : 'add'; + + if (reload) { + data.section = section; + this.http_post('domain.' + action, {data: data}); + return; + } + + this.form_error_clear(); + + if (!this.check_required_fields(data)) { + this.display_message('form.required.empty', 'error'); + return; + } + + this.set_busy(true, 'saving'); + this.api_post('domain.' + action, data, 'domain_' + action + '_response'); + }; + + this.domain_add_response = function(response) + { + if (!this.api_response(response)) + return; + + this.display_message('domain.add.success'); + this.command('domain.list', {page: this.env.list_page}); + }; + + this.domain_edit_response = function(response) + { + if (!this.api_response(response)) + return; + + this.display_message('domain.edit.success'); + this.command('domain.list', {page: this.env.list_page}); + }; + this.user_info = function(id) { this.http_post('user.info', {id: id}); @@ -1200,6 +1245,84 @@ function kolab_admin() this.command('group.list', {page: this.env.list_page}); }; + this.resource_info = function(id) + { + this.http_post('resource.info', {id: id}); + }; + + this.resource_list = function(props) + { + if (!props) + props = {}; + + if (props.search === undefined && this.env.search_request) + props.search_request = this.env.search_request; + + this.http_post('resource.list', props); + }; + + this.resource_delete = function(resourceid) + { + this.set_busy(true, 'deleting'); + this.api_post('resource.delete', {resource: resourceid}, 'resource_delete_response'); + }; + + this.resource_delete_response = function(response) + { + if (!this.api_response(response)) + return; + + var page = this.env.list_page; + + // goto previous page if last user on the current page has been deleted + if (this.env.list_count) + page -= 1; + + this.display_message('resource.delete.success'); + this.command('resource.list', {page: page}); + }; + + this.resource_save = function(reload, section) + { + var data = this.serialize_form('#'+this.env.form_id), + action = data.id ? 'edit' : 'add'; + + if (reload) { + data.section = section; + this.http_post('resource.' + action, {data: data}); + return; + } + + this.form_error_clear(); + + if (!this.check_required_fields(data)) { + this.display_message('form.required.empty', 'error'); + return; + } + + this.set_busy(true, 'saving'); + this.api_post('resource.' + action, data, 'resource_' + action + '_response'); + }; + + this.resource_add_response = function(response) + { + if (!this.api_response(response)) + return; + + this.display_message('resource.add.success'); + this.command('resource.list', {page: this.env.list_page}); + }; + + this.resource_edit_response = function(response) + { + if (!this.api_response(response)) + return; + + this.display_message('resource.edit.success'); + this.command('resource.list', {page: this.env.list_page}); + }; + + this.generate_password = function(fieldname) { this.env.password_field = fieldname; diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css index a3cd8c0..e39ece2 100644 --- a/public_html/skins/default/style.css +++ b/public_html/skins/default/style.css @@ -33,6 +33,7 @@ table.list { border: 1px solid #d0d0d0; border-spacing: 0; border-radius: 3px; + width: 100%; -moz-border-radius: 3px; -webkit-border-radius: 3px; } @@ -553,7 +554,7 @@ span.listelement input { span.listarea.disabled span.listelement input, span.listarea.readonly span.listelement input { - width: 370px; + width: 370px; } span.listelement input:focus { diff --git a/public_html/skins/default/templates/domain.html b/public_html/skins/default/templates/domain.html new file mode 100644 index 0000000..e658427 --- /dev/null +++ b/public_html/skins/default/templates/domain.html @@ -0,0 +1,17 @@ +<div id="toc" class="domain"> + <div id="search"> + <div class="searchinput"> + <input type="text" id="searchinput" name="search" value="{$engine->translate('search')}" /> + <script type="text/javascript">search_init('domain')</script> + <span class="searchactions"> + <span id="search-details" title="{$engine->translate('search.criteria')}" onclick="search_details()"></span> + <span id="search-reset" title="{$engine->translate('search.reset')}" onclick="search_reset()"></span> + </span> + </div> + <div class="searchdetails">{$engine->search_form()}</div> + </div> + <div id="domainlist"></div> +</div> +<div class="vsplitter"> </div> +<div id="taskcontent" class="domain"></div> +<div class="clear"></div> diff --git a/public_html/skins/default/templates/resource.html b/public_html/skins/default/templates/resource.html new file mode 100644 index 0000000..b2adf2f --- /dev/null +++ b/public_html/skins/default/templates/resource.html @@ -0,0 +1,17 @@ +<div id="toc" class="resource"> + <div id="search"> + <div class="searchinput"> + <input type="text" id="searchinput" name="search" value="{$engine->translate('search')}" /> + <script type="text/javascript">search_init('resource')</script> + <span class="searchactions"> + <span id="search-details" title="{$engine->translate('search.criteria')}" onclick="search_details()"></span> + <span id="search-reset" title="{$engine->translate('search.reset')}" onclick="search_reset()"></span> + </span> + </div> + <div class="searchdetails">{$engine->search_form()}</div> + </div> + <div id="resourcelist"></div> +</div> +<div class="vsplitter"> </div> +<div id="taskcontent" class="resource"></div> +<div class="clear"></div> diff --git a/public_html/skins/default/templates/role.html b/public_html/skins/default/templates/role.html new file mode 100644 index 0000000..ba8d87e --- /dev/null +++ b/public_html/skins/default/templates/role.html @@ -0,0 +1,17 @@ +<div id="toc" class="role"> + <div id="search"> + <div class="searchinput"> + <input type="text" id="searchinput" name="search" value="{$engine->translate('search')}" /> + <script type="text/javascript">search_init('role')</script> + <span class="searchactions"> + <span id="search-details" title="{$engine->translate('search.criteria')}" onclick="search_details()"></span> + <span id="search-reset" title="{$engine->translate('search.reset')}" onclick="search_reset()"></span> + </span> + </div> + <div class="searchdetails">{$engine->search_form()}</div> + </div> + <div id="rolelist"></div> +</div> +<div class="vsplitter"> </div> +<div id="taskcontent" class="role"></div> +<div class="clear"></div> |