Source for file addressbook.php

Documentation is available at addressbook.php

  1. <?php
  2. /**
  3. * functions/addressbook.php - Functions and classes for the addressbook system
  4. *
  5. * Functions require SM_PATH and support of forms.php functions
  6. *
  7. * @copyright 1999-2020 The SquirrelMail Project Team
  8. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  9. * @version $Id: addressbook.php 14840 2020年01月07日 07:42:38Z pdontthink $
  10. * @package squirrelmail
  11. * @subpackage addressbook
  12. */
  13. /**
  14. * If SM_PATH isn't defined, define it. Required to include files.
  15. * @ignore
  16. */
  17. if (!defined ('SM_PATH')) {
  18. define ('SM_PATH','../');
  19. }
  20. /* make sure that display_messages.php is loaded */
  21. include_once(SM_PATH . 'functions/display_messages.php');
  22. global $addrbook_dsn, $addrbook_global_dsn;
  23. /**
  24. Create and initialize an addressbook object.
  25. Returns the created object
  26. */
  27. function addressbook_init ($showerr = true, $onlylocal = false) {
  28. global $data_dir , $username, $color, $ldap_server, $address_book_global_filename;
  29. global $addrbook_dsn, $addrbook_table;
  30. // Shared file based address book globals
  31. // Shared DB based address book globals
  32. // Record size restriction in file based address books
  33. /* Create a new addressbook object */
  34. $abook = new AddressBook ;
  35. /* Create empty error message */
  36. $abook_init_error='';
  37. /*
  38. Always add a local backend. We use *either* file-based *or* a
  39. database addressbook. If $addrbook_dsn is set, the database
  40. backend is used. If not, addressbooks are stores in files.
  41. */
  42. if (isset($addrbook_dsn) && !empty($addrbook_dsn)) {
  43. /* Database */
  44. if (!isset($addrbook_table) || empty($addrbook_table)) {
  45. $addrbook_table = 'address';
  46. }
  47. $r = $abook->add_backend('database', Array('dsn' => $addrbook_dsn,
  48. 'owner' => $username,
  49. 'table' => $addrbook_table));
  50. if (!$r && $showerr) {
  51. $abook_init_error.=_ ("Error initializing address book database.") .' '. $abook->error;
  52. }
  53. } else {
  54. /* File */
  55. $filename = getHashedFile ($username, $data_dir, "$username.abook");
  56. $r = $abook->add_backend('local_file', Array('filename' => $filename,
  57. 'umask' => 0077,
  58. 'line_length' => $abook_file_line_length,
  59. 'create' => true));
  60. if(!$r && $showerr) {
  61. $abook_init_error.=sprintf ( _ ("Error opening file %s"), $filename );
  62. }
  63. }
  64. /* This would be for the global addressbook */
  65. if (isset($abook_global_file) && isset($abook_global_file_writeable)
  66. && trim ($abook_global_file)!=''){
  67. // Detect place of address book
  68. if (! preg_match ("/[\/\\\]/",$abook_global_file)) {
  69. /* no path chars, address book stored in data directory
  70. * make sure that there is a slash between data directory
  71. * and address book file name
  72. */
  73. $abook_global_filename=$data_dir
  74. . ((substr ($data_dir, -1) != '/') ? '/' : '')
  75. . $abook_global_file;
  76. } elseif (preg_match ("/^\/|\w:/",$abook_global_file)) {
  77. // full path is set in options (starts with slash or x:)
  78. $abook_global_filename=$abook_global_file;
  79. } else {
  80. $abook_global_filename=SM_PATH . $abook_global_file;
  81. }
  82. $r = $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
  83. 'name' => _ ("Global address book"),
  84. 'detect_writeable' => false,
  85. 'line_length' => $abook_file_line_length,
  86. 'writeable'=> $abook_global_file_writeable,
  87. 'listing' => $abook_global_file_listing));
  88. if (!$r && $showerr) {
  89. if ($abook_init_error!='') $abook_init_error.="\n";
  90. $abook_init_error.=_ ("Error initializing global address book.") . "\n" . $abook->error;
  91. }
  92. }
  93. /* Load global addressbook from SQL if configured */
  94. if (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn)) {
  95. /* Database configured */
  96. if (!isset($addrbook_global_table) || empty($addrbook_global_table)) {
  97. $addrbook_global_table = 'global_abook';
  98. }
  99. $r = $abook->add_backend('database',
  100. Array('dsn' => $addrbook_global_dsn,
  101. 'owner' => 'global',
  102. 'name' => _ ("Global address book"),
  103. 'writeable' => $addrbook_global_writeable,
  104. 'listing' => $addrbook_global_listing,
  105. 'table' => $addrbook_global_table));
  106. if (!$r && $showerr) {
  107. if ($abook_init_error!='') $abook_init_error.="\n";
  108. $abook_init_error.=_ ("Error initializing global address book.") . "\n" . $abook->error;
  109. }
  110. }
  111. /*
  112. * hook allows to include different address book backends.
  113. * plugins should extract $abook and $r from arguments
  114. * and use same add_backend commands as above functions.
  115. * @since 1.5.1 and 1.4.5
  116. */
  117. $hookReturn = do_hook ('abook_init', $abook, $r);
  118. $abook = $hookReturn[1];
  119. $r = $hookReturn[2];
  120. if (! $onlylocal) {
  121. /* Load configured LDAP servers (if PHP has LDAP support) */
  122. if (isset($ldap_server) && is_array ($ldap_server) && function_exists ('ldap_connect')) {
  123. reset ($ldap_server);
  124. while (list($undef,$param) = each ($ldap_server)) {
  125. if (is_array ($param)) {
  126. $r = $abook->add_backend('ldap_server', $param);
  127. if (!$r && $showerr) {
  128. if ($abook_init_error!='') $abook_init_error.="\n";
  129. $abook_init_error.=sprintf (_ ("Error initializing LDAP server %s:") .
  130. "\n", $param['host']);
  131. $abook_init_error.= $abook->error;
  132. }
  133. }
  134. }
  135. }
  136. } // end of remote abook backends init
  137. /**
  138. * display address book init errors.
  139. */
  140. if ($abook_init_error!='' && $showerr) {
  141. $abook_init_error = sm_encode_html_special_chars ($abook_init_error);
  142. error_box ($abook_init_error,$color);
  143. }
  144. /* Return the initialized object */
  145. return $abook;
  146. }
  147. /*
  148. * Had to move this function outside of the Addressbook Class
  149. * PHP 4.0.4 Seemed to be having problems with inline functions.
  150. */
  151. function addressbook_cmp ($a,$b) {
  152. if($a['backend'] > $b['backend']) {
  153. return 1;
  154. } else if($a['backend'] < $b['backend']) {
  155. return -1;
  156. }
  157. return (strtolower ($a['name']) > strtolower ($b['name'])) ? 1 : -1;
  158. }
  159. /**
  160. * Sort array by the key "name"
  161. */
  162. function alistcmp ($a,$b) {
  163. $abook_sort_order=get_abook_sort ();
  164. switch ($abook_sort_order) {
  165. case 0:
  166. case 1:
  167. $abook_sort='nickname';
  168. break;
  169. case 4:
  170. case 5:
  171. $abook_sort='email';
  172. break;
  173. case 6:
  174. case 7:
  175. $abook_sort='label';
  176. break;
  177. case 2:
  178. case 3:
  179. case 8:
  180. default:
  181. $abook_sort='name';
  182. }
  183. if ($a['backend'] > $b['backend']) {
  184. return 1;
  185. } else {
  186. if ($a['backend'] < $b['backend']) {
  187. return -1;
  188. }
  189. }
  190. if( (($abook_sort_order+2) % 2) == 1) {
  191. return (strtolower ($a[$abook_sort]) < strtolower ($b[$abook_sort])) ? 1 : -1;
  192. } else {
  193. return (strtolower ($a[$abook_sort]) > strtolower ($b[$abook_sort])) ? 1 : -1;
  194. }
  195. }
  196. /**
  197. * Address book sorting options
  198. *
  199. * returns address book sorting order
  200. * @return integer book sorting options order
  201. */
  202. function get_abook_sort () {
  203. global $data_dir , $username;
  204. /* get sorting order */
  205. if(sqgetGlobalVar ('abook_sort_order', $temp, SQ_GET )) {
  206. $abook_sort_order = (int) $temp;
  207. if ($abook_sort_order < 0 or $abook_sort_order > 8)
  208. $abook_sort_order=8;
  209. setPref ($data_dir, $username, 'abook_sort_order', $abook_sort_order);
  210. } else {
  211. /* get previous sorting options. default to unsorted */
  212. $abook_sort_order = getPref ($data_dir, $username, 'abook_sort_order', 8);
  213. }
  214. return $abook_sort_order;
  215. }
  216. /**
  217. * This function shows the address book sort button.
  218. *
  219. * @param integer $abook_sort_order current sort value
  220. * @param string $alt_tag alt tag value (string visible to text only browsers)
  221. * @param integer $Down sort value when list is sorted ascending
  222. * @param integer $Up sort value when list is sorted descending
  223. * @return string html code with sorting images and urls
  224. * @since 1.5.1 and 1.4.6
  225. */
  226. function show_abook_sort_button ($abook_sort_order, $alt_tag, $Down, $Up ) {
  227. global $form_url;
  228. /* Figure out which image we want to use. */
  229. if ($abook_sort_order != $Up && $abook_sort_order != $Down) {
  230. $img = 'sort_none.png';
  231. $which = $Up;
  232. } elseif ($abook_sort_order == $Up) {
  233. $img = 'up_pointer.png';
  234. $which = $Down;
  235. } else {
  236. $img = 'down_pointer.png';
  237. $which = 8;
  238. }
  239. /* Now that we have everything figured out, show the actual button. */
  240. return ' <a href="' . $form_url .'?abook_sort_order=' . $which
  241. . '"><img src="../images/' . $img
  242. . '" border="0" width="12" height="10" alt="' . $alt_tag . '" title="'
  243. . _ ("Click here to change the sorting of the address list") .'" /></a>';
  244. }
  245. /**
  246. * This is the main address book class that connect all the
  247. * backends and provide services to the functions above.
  248. * @package squirrelmail
  249. */
  250. class AddressBook {
  251. var $backends = array();
  252. var $numbackends = 0;
  253. var $error = '';
  254. var $localbackend = 0;
  255. var $add_extra_field = false;
  256. /**
  257. * Constructor (PHP5 style, required in some future version of PHP)
  258. */
  259. function __construct () {
  260. $this->localbackendname = _ ("Personal address book");
  261. }
  262. /**
  263. * Constructor (PHP4 style, kept for compatibility reasons)
  264. */
  265. function AddressBook () {
  266. self::__construct();
  267. }
  268. /*
  269. * Return an array of backends of a given type,
  270. * or all backends if no type is specified.
  271. */
  272. function get_backend_list ($type = '') {
  273. $ret = array();
  274. for ($i = 1 ; $i <= $this->numbackends ; $i++) {
  275. if (empty($type) || $type == $this->backends [$i]->btype) {
  276. $ret[] = &$this->backends [$i];
  277. }
  278. }
  279. return $ret;
  280. }
  281. /*
  282. ========================== Public ========================
  283. Add a new backend. $backend is the name of a backend
  284. (without the abook_ prefix), and $param is an optional
  285. mixed variable that is passed to the backend constructor.
  286. See each of the backend classes for valid parameters.
  287. */
  288. function add_backend ($backend, $param = '') {
  289. $backend_name = 'abook_' . $backend;
  290. eval('$newback = new ' . $backend_name . '($param);');
  291. if(!empty($newback->error)) {
  292. $this->error = $newback->error;
  293. return false;
  294. }
  295. $this->numbackends++;
  296. $newback->bnum = $this->numbackends ;
  297. $this->backends [$this->numbackends ] = $newback;
  298. /* Store ID of first local backend added */
  299. if ($this->localbackend == 0 && $newback->btype == 'local') {
  300. $this->localbackend = $this->numbackends ;
  301. $this->localbackendname = $newback->sname;
  302. }
  303. return $this->numbackends ;
  304. }
  305. /*
  306. * This function takes a $row array as returned by the addressbook
  307. * search and returns an e-mail address with the full name or
  308. * nickname optionally prepended.
  309. */
  310. static function full_address ($row) {
  311. global $data_dir , $username;
  312. $addrsrch_fullname = getPref ($data_dir, $username, 'addrsrch_fullname', 'fullname');
  313. // allow multiple addresses in one row (poor person's grouping - bah)
  314. // (separate with commas)
  315. //
  316. $return = '';
  317. $addresses = explode (',', $row['email']);
  318. foreach ($addresses as $address) {
  319. if (!empty($return)) $return .= ', ';
  320. if ($addrsrch_fullname == 'fullname')
  321. $return .= '"' . $row['name'] . '" <' . trim ($address) . '>';
  322. else if ($addrsrch_fullname == 'nickname')
  323. $return .= '"' . $row['nickname'] . '" <' . trim ($address) . '>';
  324. else // "noprefix"
  325. $return .= trim ($address);
  326. }
  327. return $return;
  328. }
  329. /*
  330. Return a list of addresses matching expression in
  331. all backends of a given type.
  332. */
  333. function search ($expression, $bnum = -1) {
  334. $ret = array();
  335. $this->error = '';
  336. /* Search all backends */
  337. if ($bnum == -1) {
  338. $sel = $this->get_backend_list ('');
  339. $failed = 0;
  340. for ($i = 0 ; $i < sizeof ($sel) ; $i++) {
  341. $backend = &$sel[$i];
  342. $backend->error = '';
  343. $res = $backend->search($expression);
  344. if (is_array ($res)) {
  345. $ret = array_merge ($ret, $res);
  346. } else {
  347. $this->error .= "\n" . $backend->error;
  348. $failed++;
  349. }
  350. }
  351. /* Only fail if all backends failed */
  352. if( $failed >= sizeof ( $sel ) ) {
  353. $ret = FALSE;
  354. }
  355. } else {
  356. /* Search only one backend */
  357. $ret = $this->backends [$bnum]->search($expression);
  358. if (!is_array ($ret)) {
  359. $this->error .= "\n" . $this->backends [$bnum]->error;
  360. $ret = FALSE;
  361. }
  362. }
  363. return( $ret );
  364. }
  365. /* Return a sorted search */
  366. function s_search ($expression, $bnum = -1) {
  367. $ret = $this->search ($expression, $bnum);
  368. if ( is_array ( $ret ) ) {
  369. usort ($ret, 'addressbook_cmp');
  370. }
  371. return $ret;
  372. }
  373. /*
  374. * Lookup an address by the indicated field. Only
  375. * possible in local backends.
  376. */
  377. function lookup ($value, $bnum = -1, $field = SM_ABOOK_FIELD_NICKNAME) {
  378. $ret = array();
  379. if ($bnum > -1) {
  380. $res = $this->backends [$bnum]->lookup($value, $field);
  381. if (is_array ($res)) {
  382. return $res;
  383. } else {
  384. $this->error = $this->backends [$bnum]->error;
  385. return false;
  386. }
  387. }
  388. $sel = $this->get_backend_list ('local');
  389. for ($i = 0 ; $i < sizeof ($sel) ; $i++) {
  390. $backend = &$sel[$i];
  391. $backend->error = '';
  392. $res = $backend->lookup($value, $field);
  393. // return an address if one is found
  394. // (empty array means lookup concluded
  395. // but no result found - in this case,
  396. // proceed to next backend)
  397. //
  398. if (is_array ($res)) {
  399. if (!empty($res)) return $res;
  400. } else {
  401. $this->error = $backend->error;
  402. return false;
  403. }
  404. }
  405. return $ret;
  406. }
  407. /* Return all addresses */
  408. function list_addr ($bnum = -1) {
  409. $ret = array();
  410. if ($bnum == -1) {
  411. $sel = $this->get_backend_list ('');
  412. } else {
  413. $sel = array(0 => &$this->backends [$bnum]);
  414. }
  415. for ($i = 0 ; $i < sizeof ($sel) ; $i++) {
  416. $backend = &$sel[$i];
  417. $backend->error = '';
  418. $res = $backend->list_addr();
  419. if (is_array ($res)) {
  420. $ret = array_merge ($ret, $res);
  421. } else {
  422. $this->error = $backend->error;
  423. return false;
  424. }
  425. }
  426. return $ret;
  427. }
  428. /*
  429. * Create a new address from $userdata, in backend $bnum.
  430. * Return the backend number that the/ address was added
  431. * to, or false if it failed.
  432. */
  433. function add ($userdata, $bnum) {
  434. /* Validate data */
  435. if (!is_array ($userdata)) {
  436. $this->error = _ ("Invalid input data");
  437. return false;
  438. }
  439. if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
  440. $this->error = _ ("Name is missing");
  441. return false;
  442. }
  443. if (empty($userdata['email'])) {
  444. $this->error = _ ("E-mail address is missing");
  445. return false;
  446. }
  447. if (empty($userdata['nickname'])) {
  448. $userdata['nickname'] = $userdata['email'];
  449. }
  450. /* Check that specified backend accept new entries */
  451. if (!$this->backends [$bnum]->writeable) {
  452. $this->error = _ ("Address book is read-only");
  453. return false;
  454. }
  455. /* Add address to backend */
  456. $res = $this->backends [$bnum]->add($userdata);
  457. if ($res) {
  458. return $bnum;
  459. } else {
  460. $this->error = $this->backends [$bnum]->error;
  461. return false;
  462. }
  463. return false; // Not reached
  464. } /* end of add() */
  465. /*
  466. * Remove the user identified by $alias from backend $bnum
  467. * If $alias is an array, all users in the array are removed.
  468. */
  469. function remove ($alias, $bnum) {
  470. /* Check input */
  471. if (empty($alias)) {
  472. return true;
  473. }
  474. /* Convert string to single element array */
  475. if (!is_array ($alias)) {
  476. $alias = array(0 => $alias);
  477. }
  478. /* Check that specified backend is writable */
  479. if (!$this->backends [$bnum]->writeable) {
  480. $this->error = _ ("Address book is read-only");
  481. return false;
  482. }
  483. /* Remove user from backend */
  484. $res = $this->backends [$bnum]->remove($alias);
  485. if ($res) {
  486. return $bnum;
  487. } else {
  488. $this->error = $this->backends [$bnum]->error;
  489. return false;
  490. }
  491. return FALSE; /* Not reached */
  492. } /* end of remove() */
  493. /*
  494. * Remove the user identified by $alias from backend $bnum
  495. * If $alias is an array, all users in the array are removed.
  496. */
  497. function modify ($alias, $userdata, $bnum) {
  498. /* Check input */
  499. if (empty($alias) || !is_string ($alias)) {
  500. return true;
  501. }
  502. /* Validate data */
  503. if(!is_array ($userdata)) {
  504. $this->error = _ ("Invalid input data");
  505. return false;
  506. }
  507. if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
  508. $this->error = _ ("Name is missing");
  509. return false;
  510. }
  511. if (empty($userdata['email'])) {
  512. $this->error = _ ("E-mail address is missing");
  513. return false;
  514. }
  515. if (empty($userdata['nickname'])) {
  516. $userdata['nickname'] = $userdata['email'];
  517. }
  518. /* Check that specified backend is writable */
  519. if (!$this->backends [$bnum]->writeable) {
  520. $this->error = _ ("Address book is read-only");;
  521. return false;
  522. }
  523. /* Modify user in backend */
  524. $res = $this->backends [$bnum]->modify($alias, $userdata);
  525. if ($res) {
  526. return $bnum;
  527. } else {
  528. $this->error = $this->backends [$bnum]->error;
  529. return false;
  530. }
  531. return FALSE; /* Not reached */
  532. } /* end of modify() */
  533. } /* End of class Addressbook */
  534. /**
  535. * Generic backend that all other backends extend
  536. * @package squirrelmail
  537. */
  538. /* Variables that all backends must provide. */
  539. var $btype = 'dummy';
  540. var $bname = 'dummy';
  541. var $sname = 'Dummy backend';
  542. /*
  543. * Variables common for all backends, but that
  544. * should not be changed by the backends.
  545. */
  546. var $bnum = -1;
  547. var $error = '';
  548. var $writeable = false;
  549. function set_error ($string) {
  550. $this->error = '[' . $this->sname . '] ' . $string;
  551. return false;
  552. }
  553. /* ========================== Public ======================== */
  554. function search ($expression) {
  555. $this->set_error ('search not implemented');
  556. return false;
  557. }
  558. function lookup ($value, $field=SM_ABOOK_FIELD_NICKNAME) {
  559. $this->set_error ('lookup not implemented');
  560. return false;
  561. }
  562. function list_addr () {
  563. $this->set_error ('list_addr not implemented');
  564. return false;
  565. }
  566. function add ($userdata) {
  567. $this->set_error ('add not implemented');
  568. return false;
  569. }
  570. function remove ($alias) {
  571. $this->set_error ('delete not implemented');
  572. return false;
  573. }
  574. function modify ($alias, $newuserdata) {
  575. $this->set_error ('modify not implemented');
  576. return false;
  577. }
  578. }
  579. /*
  580. PHP 5 requires that the class be made first, which seems rather
  581. logical, and should have been the way it was generated the first time.
  582. */
  583. require_once(SM_PATH . 'functions/abook_local_file.php');
  584. require_once(SM_PATH . 'functions/abook_ldap_server.php');
  585. /* Only load database backend if database is configured */
  586. if((isset($addrbook_dsn) && !empty($addrbook_dsn)) ||
  587. (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn))) {
  588. include_once(SM_PATH . 'functions/abook_database.php');
  589. }
  590. /*
  591. * hook allows adding different address book classes.
  592. * class must follow address book class coding standards.
  593. *
  594. * see addressbook_backend class and functions/abook_*.php files.
  595. * @since 1.5.1 and 1.4.5
  596. */
  597. do_hook ('abook_add_class');

Documentation generated on 2020年1月13日 04:24:13 +0100 by phpDocumentor 1.4.3

AltStyle によって変換されたページ (->オリジナル) /