addressbook.php 13 KB

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