addressbook.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <?php
  2. /**
  3. * addressbook.php
  4. *
  5. * Copyright (c) 1999-2001 The Squirrelmail Development Team
  6. * Licensed under the GNU GPL. For full terms see the file COPYING.
  7. *
  8. * Functions and classes for the addressbook system.
  9. *
  10. * $Id$
  11. */
  12. // This is the path to the global site-wide addressbook.
  13. // It looks and feels just like a user's .abook file
  14. // If this is in the data directory, use "$data_dir/global.abook"
  15. // If not, specify the path as though it was accessed from the
  16. // src/ directory ("../global.abook" -> in main directory)
  17. //
  18. // If you don't want a global site-wide addressbook, comment these
  19. // two lines out. (They are disabled by default.)
  20. //
  21. // The global addressbook is unmodifiable by anyone. You must actually
  22. // use a shell script or whatnot to modify the contents.
  23. //
  24. //global $data_dir;
  25. //$address_book_global_filename = "$data_dir/global.abook";
  26. // Include backends here.
  27. require_once('../functions/abook_local_file.php');
  28. require_once('../functions/abook_ldap_server.php');
  29. // Use this if you wanna have a global address book
  30. if (isset($address_book_global_filename))
  31. include_once('../functions/abook_global_file.php');
  32. // Only load database backend if database is configured
  33. global $addrbook_dsn;
  34. if(isset($addrbook_dsn))
  35. include_once('../functions/abook_database.php');
  36. // Create and initialize an addressbook object.
  37. // Returns the created object
  38. function addressbook_init($showerr = true, $onlylocal = false) {
  39. global $data_dir, $username, $ldap_server, $address_book_global_filename;
  40. global $addrbook_dsn;
  41. // Create a new addressbook object
  42. $abook = new AddressBook;
  43. // Always add a local backend. We use *either* file-based *or* a
  44. // database addressbook. If $addrbook_dsn is set, the database
  45. // backend is used. If not, addressbooks are stores in files.
  46. if(isset($addrbook_dsn) && !empty($addrbook_dsn)) {
  47. // Database
  48. $r = $abook->add_backend('database', Array('dsn' => $addrbook_dsn,
  49. 'owner' => $username,
  50. 'table' => 'address'));
  51. if(!$r && $showerr) {
  52. printf(_("Error initializing addressbook database."));
  53. exit;
  54. }
  55. } else {
  56. // File
  57. $filename = sprintf('%s%s.abook', $data_dir, $username);
  58. $r = $abook->add_backend('local_file', Array('filename' => $filename,
  59. 'create' => true));
  60. if(!$r && $showerr) {
  61. printf(_("Error opening file %s"), $filename);
  62. exit;
  63. }
  64. }
  65. // This would be for the global addressbook
  66. if (isset($address_book_global_filename)) {
  67. $r = $abook->add_backend('global_file');
  68. if (!$r && $showerr) {
  69. printf(_("Error initializing global addressbook."));
  70. exit;
  71. }
  72. }
  73. if($onlylocal)
  74. return $abook;
  75. // Load configured LDAP servers (if PHP has LDAP support)
  76. if(isset($ldap_server) && is_array($ldap_server) &&
  77. function_exists('ldap_connect')) {
  78. reset($ldap_server);
  79. while(list($undef,$param) = each($ldap_server)) {
  80. if(is_array($param)) {
  81. $r = $abook->add_backend('ldap_server', $param);
  82. if(!$r && $showerr) {
  83. printf('&nbsp;' . _("Error initializing LDAP server %s:") .
  84. "<BR>\n", $param['host']);
  85. print('&nbsp;' . $abook->error);
  86. exit;
  87. }
  88. }
  89. }
  90. }
  91. // Return the initialized object
  92. return $abook;
  93. }
  94. // Had to move this function outside of the Addressbook Class
  95. // PHP 4.0.4 Seemed to be having problems with inline functions.
  96. function addressbook_cmp($a,$b) {
  97. if($a['backend'] > $b['backend'])
  98. return 1;
  99. else if($a['backend'] < $b['backend'])
  100. return -1;
  101. return (strtolower($a['name']) > strtolower($b['name'])) ? 1 : -1;
  102. }
  103. /**
  104. ** This is the main address book class that connect all the
  105. ** backends and provide services to the functions above.
  106. **
  107. **/
  108. class AddressBook {
  109. var $backends = array();
  110. var $numbackends = 0;
  111. var $error = '';
  112. var $localbackend = 0;
  113. var $localbackendname = '';
  114. // Constructor function.
  115. function AddressBook() {
  116. $localbackendname = _("Personal address book");
  117. }
  118. // Return an array of backends of a given type,
  119. // or all backends if no type is specified.
  120. function get_backend_list($type = '') {
  121. $ret = array();
  122. for($i = 1 ; $i <= $this->numbackends ; $i++) {
  123. if(empty($type) || $type == $this->backends[$i]->btype) {
  124. $ret[] = &$this->backends[$i];
  125. }
  126. }
  127. return $ret;
  128. }
  129. // ========================== Public ========================
  130. // Add a new backend. $backend is the name of a backend
  131. // (without the abook_ prefix), and $param is an optional
  132. // mixed variable that is passed to the backend constructor.
  133. // See each of the backend classes for valid parameters.
  134. function add_backend($backend, $param = '') {
  135. $backend_name = 'abook_' . $backend;
  136. eval('$newback = new ' . $backend_name . '($param);');
  137. if(!empty($newback->error)) {
  138. $this->error = $newback->error;
  139. return false;
  140. }
  141. $this->numbackends++;
  142. $newback->bnum = $this->numbackends;
  143. $this->backends[$this->numbackends] = $newback;
  144. // Store ID of first local backend added
  145. if($this->localbackend == 0 && $newback->btype == 'local') {
  146. $this->localbackend = $this->numbackends;
  147. $this->localbackendname = $newback->sname;
  148. }
  149. return $this->numbackends;
  150. }
  151. // Return a list of addresses matching expression in
  152. // all backends of a given type.
  153. function search($expression, $bnum = -1) {
  154. $ret = array();
  155. $this->error = '';
  156. // Search all backends
  157. if($bnum == -1) {
  158. $sel = $this->get_backend_list('');
  159. $failed = 0;
  160. for($i = 0 ; $i < sizeof($sel) ; $i++) {
  161. $backend = &$sel[$i];
  162. $backend->error = '';
  163. $res = $backend->search($expression);
  164. if(is_array($res)) {
  165. $ret = array_merge($ret, $res);
  166. } else {
  167. $this->error .= "<br>\n" . $backend->error;
  168. $failed++;
  169. }
  170. }
  171. // Only fail if all backends failed
  172. if($failed >= sizeof($sel))
  173. return false;
  174. }
  175. // Search only one backend
  176. else {
  177. $ret = $this->backends[$bnum]->search($expression);
  178. if(!is_array($ret)) {
  179. $this->error .= "<br>\n" . $this->backends[$bnum]->error;
  180. return false;
  181. }
  182. }
  183. return $ret;
  184. }
  185. // Return a sorted search
  186. function s_search($expression, $bnum = -1) {
  187. $ret = $this->search($expression, $bnum);
  188. if(!is_array($ret))
  189. return $ret;
  190. usort($ret, 'addressbook_cmp');
  191. return $ret;
  192. }
  193. // Lookup an address by alias. Only possible in
  194. // local backends.
  195. function lookup($alias, $bnum = -1) {
  196. $ret = array();
  197. if($bnum > -1) {
  198. $res = $this->backends[$bnum]->lookup($alias);
  199. if(is_array($res)) {
  200. return $res;
  201. } else {
  202. $this->error = $backend->error;
  203. return false;
  204. }
  205. }
  206. $sel = $this->get_backend_list('local');
  207. for($i = 0 ; $i < sizeof($sel) ; $i++) {
  208. $backend = &$sel[$i];
  209. $backend->error = '';
  210. $res = $backend->lookup($alias);
  211. if(is_array($res)) {
  212. if(!empty($res))
  213. return $res;
  214. } else {
  215. $this->error = $backend->error;
  216. return false;
  217. }
  218. }
  219. return $ret;
  220. }
  221. // Return all addresses
  222. function list_addr($bnum = -1) {
  223. $ret = array();
  224. if($bnum == -1)
  225. $sel = $this->get_backend_list('local');
  226. else
  227. $sel = array(0 => &$this->backends[$bnum]);
  228. for($i = 0 ; $i < sizeof($sel) ; $i++) {
  229. $backend = &$sel[$i];
  230. $backend->error = '';
  231. $res = $backend->list_addr();
  232. if(is_array($res)) {
  233. $ret = array_merge($ret, $res);
  234. } else {
  235. $this->error = $backend->error;
  236. return false;
  237. }
  238. }
  239. return $ret;
  240. }
  241. // Create a new address from $userdata, in backend $bnum.
  242. // Return the backend number that the/ address was added
  243. // to, or false if it failed.
  244. function add($userdata, $bnum) {
  245. // Validate data
  246. if(!is_array($userdata)) {
  247. $this->error = _("Invalid input data");
  248. return false;
  249. }
  250. if(empty($userdata['firstname']) &&
  251. empty($userdata['lastname'])) {
  252. $this->error = _("Name is missing");
  253. return false;
  254. }
  255. if(empty($userdata['email'])) {
  256. $this->error = _("E-mail address is missing");
  257. return false;
  258. }
  259. if(empty($userdata['nickname'])) {
  260. $userdata['nickname'] = $userdata['email'];
  261. }
  262. if(eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
  263. $this->error = _("Nickname contains illegal characters");
  264. return false;
  265. }
  266. // Check that specified backend accept new entries
  267. if(!$this->backends[$bnum]->writeable) {
  268. $this->error = _("Addressbook is read-only");
  269. return false;
  270. }
  271. // Add address to backend
  272. $res = $this->backends[$bnum]->add($userdata);
  273. if($res) {
  274. return $bnum;
  275. } else {
  276. $this->error = $this->backends[$bnum]->error;
  277. return false;
  278. }
  279. return false; // Not reached
  280. } // end of add()
  281. // Remove the user identified by $alias from backend $bnum
  282. // If $alias is an array, all users in the array are removed.
  283. function remove($alias, $bnum) {
  284. // Check input
  285. if(empty($alias))
  286. return true;
  287. // Convert string to single element array
  288. if(!is_array($alias))
  289. $alias = array(0 => $alias);
  290. // Check that specified backend is writable
  291. if(!$this->backends[$bnum]->writeable) {
  292. $this->error = _("Addressbook is read-only");
  293. return false;
  294. }
  295. // Remove user from backend
  296. $res = $this->backends[$bnum]->remove($alias);
  297. if($res) {
  298. return $bnum;
  299. } else {
  300. $this->error = $this->backends[$bnum]->error;
  301. return false;
  302. }
  303. return false; // Not reached
  304. } // end of remove()
  305. // Remove the user identified by $alias from backend $bnum
  306. // If $alias is an array, all users in the array are removed.
  307. function modify($alias, $userdata, $bnum) {
  308. // Check input
  309. if(empty($alias) || !is_string($alias))
  310. return true;
  311. // Validate data
  312. if(!is_array($userdata)) {
  313. $this->error = _("Invalid input data");
  314. return false;
  315. }
  316. if(empty($userdata['firstname']) &&
  317. empty($userdata['lastname'])) {
  318. $this->error = _("Name is missing");
  319. return false;
  320. }
  321. if(empty($userdata['email'])) {
  322. $this->error = _("E-mail address is missing");
  323. return false;
  324. }
  325. if(eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
  326. $this->error = _("Nickname contains illegal characters");
  327. return false;
  328. }
  329. if(empty($userdata['nickname'])) {
  330. $userdata['nickname'] = $userdata['email'];
  331. }
  332. // Check that specified backend is writable
  333. if(!$this->backends[$bnum]->writeable) {
  334. $this->error = _("Addressbook is read-only");;
  335. return false;
  336. }
  337. // Modify user in backend
  338. $res = $this->backends[$bnum]->modify($alias, $userdata);
  339. if($res) {
  340. return $bnum;
  341. } else {
  342. $this->error = $this->backends[$bnum]->error;
  343. return false;
  344. }
  345. return false; // Not reached
  346. } // end of modify()
  347. } // End of class Addressbook
  348. /**
  349. ** Generic backend that all other backends extend
  350. **/
  351. class addressbook_backend {
  352. // Variables that all backends must provide.
  353. var $btype = 'dummy';
  354. var $bname = 'dummy';
  355. var $sname = 'Dummy backend';
  356. // Variables common for all backends, but that
  357. // should not be changed by the backends.
  358. var $bnum = -1;
  359. var $error = '';
  360. var $writeable = false;
  361. function set_error($string) {
  362. $this->error = '[' . $this->sname . '] ' . $string;
  363. return false;
  364. }
  365. // ========================== Public ========================
  366. function search($expression) {
  367. $this->set_error('search not implemented');
  368. return false;
  369. }
  370. function lookup($alias) {
  371. $this->set_error('lookup not implemented');
  372. return false;
  373. }
  374. function list_addr() {
  375. $this->set_error('list_addr not implemented');
  376. return false;
  377. }
  378. function add($userdata) {
  379. $this->set_error('add not implemented');
  380. return false;
  381. }
  382. function remove($alias) {
  383. $this->set_error('delete not implemented');
  384. return false;
  385. }
  386. function modify($alias, $newuserdata) {
  387. $this->set_error('modify not implemented');
  388. return false;
  389. }
  390. }
  391. ?>