phpDocumentor updates
[squirrelmail.git] / functions / addressbook.php
1 <?php
2
3 /**
4 * functions/addressbook.php - Functions and classes for the addressbook system
5 *
6 * Functions require SM_PATH and support of forms.php functions
7 *
8 * @copyright &copy; 1999-2005 The SquirrelMail Project Team
9 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
10 * @version $Id$
11 * @package squirrelmail
12 * @subpackage addressbook
13 */
14
15 /**
16 * If SM_PATH isn't defined, define it. Required to include files.
17 * @ignore
18 */
19 if (!defined('SM_PATH')) {
20 define('SM_PATH','../');
21 }
22
23 /* make sure that display_messages.php is loaded */
24 include_once(SM_PATH . 'functions/display_messages.php');
25
26 global $addrbook_dsn, $addrbook_global_dsn;
27
28 /**
29 * Create and initialize an addressbook object.
30 * @param boolean $showerr display any address book init errors. html page header
31 * must be created before calling addressbook_init() with $showerr enabled.
32 * @param boolean $onlylocal enable only local address book backends
33 * @return object address book object.
34 */
35 function addressbook_init($showerr = true, $onlylocal = false) {
36 global $data_dir, $username, $color, $ldap_server, $address_book_global_filename;
37 global $addrbook_dsn, $addrbook_table;
38 global $abook_global_file, $abook_global_file_writeable, $abook_global_file_listing;
39 global $addrbook_global_dsn, $addrbook_global_table, $addrbook_global_writeable, $addrbook_global_listing;
40
41 /* Create a new addressbook object */
42 $abook = new AddressBook;
43
44 /* Create empty error message */
45 $abook_init_error='';
46
47 /*
48 Always add a local backend. We use *either* file-based *or* a
49 database addressbook. If $addrbook_dsn is set, the database
50 backend is used. If not, addressbooks are stores in files.
51 */
52 if (isset($addrbook_dsn) && !empty($addrbook_dsn)) {
53 /* Database */
54 if (!isset($addrbook_table) || empty($addrbook_table)) {
55 $addrbook_table = 'address';
56 }
57 $r = $abook->add_backend('database', Array('dsn' => $addrbook_dsn,
58 'owner' => $username,
59 'table' => $addrbook_table));
60 if (!$r && $showerr) {
61 $abook_init_error.=_("Error initializing addressbook database.") . "<br />\n" . $abook->error;
62 }
63 } else {
64 /* File */
65 $filename = getHashedFile($username, $data_dir, "$username.abook");
66 $r = $abook->add_backend('local_file', Array('filename' => $filename,
67 'create' => true));
68 if(!$r && $showerr) {
69 // no need to use $abook->error, because message explains error.
70 $abook_init_error.=sprintf( _("Error opening file %s"), $filename );
71 }
72 }
73
74 /* Global file based addressbook */
75 if (isset($abook_global_file) &&
76 isset($abook_global_file_writeable) &&
77 isset($abook_global_file_listing) &&
78 trim($abook_global_file)!=''){
79
80 // Detect place of address book
81 if (! preg_match("/[\/\\\]/",$abook_global_file)) {
82 /* no path chars, address book stored in data directory
83 * make sure that there is a slash between data directory
84 * and address book file name
85 */
86 $abook_global_filename=$data_dir
87 . ((substr($data_dir, -1) != '/') ? '/' : '')
88 . $abook_global_file;
89 } elseif (preg_match("/^\/|\w:/",$abook_global_file)) {
90 // full path is set in options (starts with slash or x:)
91 $abook_global_filename=$abook_global_file;
92 } else {
93 $abook_global_filename=SM_PATH . $abook_global_file;
94 }
95
96 $r = $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
97 'name' => _("Global address book"),
98 'detect_writeable' => false,
99 'writeable'=> $abook_global_file_writeable,
100 'listing' => $abook_global_file_listing));
101
102 /* global abook init error is not fatal. add error message and continue */
103 if (!$r && $showerr) {
104 if ($abook_init_error!='') $abook_init_error.="<br />\n";
105 $abook_init_error.=_("Error initializing global addressbook.") . "<br />\n" . $abook->error;
106 }
107 }
108
109 /* Load global addressbook from SQL if configured */
110 if (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn)) {
111 /* Database configured */
112 if (!isset($addrbook_global_table) || empty($addrbook_global_table)) {
113 $addrbook_global_table = 'global_abook';
114 }
115 $r = $abook->add_backend('database',
116 Array('dsn' => $addrbook_global_dsn,
117 'owner' => 'global',
118 'name' => _("Global address book"),
119 'writeable' => $addrbook_global_writeable,
120 'listing' => $addrbook_global_listing,
121 'table' => $addrbook_global_table));
122 /* global abook init error is not fatal. add error message and continue */
123 if (!$r && $showerr) {
124 if ($abook_init_error!='') $abook_init_error.="<br />\n";
125 $abook_init_error.=_("Error initializing global addressbook.") . "<br />\n" . $abook->error;
126 }
127 }
128
129 /*
130 * hook allows to include different address book backends.
131 * plugins should extract $abook and $r from arguments
132 * and use same add_backend commands as above functions.
133 * @since 1.5.1 and 1.4.5
134 */
135 $hookReturn = do_hook('abook_init', $abook, $r);
136 $abook = $hookReturn[1];
137 $r = $hookReturn[2];
138
139 if (! $onlylocal) {
140 /* Load configured LDAP servers (if PHP has LDAP support) */
141 if (isset($ldap_server) && is_array($ldap_server)) {
142 reset($ldap_server);
143 while (list($undef,$param) = each($ldap_server)) {
144 if (is_array($param)) {
145 $r = $abook->add_backend('ldap_server', $param);
146 if (!$r && $showerr) {
147 if ($abook_init_error!='') $abook_init_error.="<br />\n";
148 $abook_init_error.=sprintf(_("Error initializing LDAP server %s:") .
149 "<br />\n", $param['host']);
150 $abook_init_error.= $abook->error;
151 }
152 }
153 }
154 } // end of ldap server init
155 } // end of remote abook backend init
156
157 /**
158 * display address book init errors.
159 */
160 if ($abook_init_error!='' && $showerr) {
161 error_box($abook_init_error,$color);
162 }
163
164 /* Return the initialized object */
165 return $abook;
166 }
167
168 /**
169 * Display the "new address" form
170 *
171 * Form is not closed and you must add closing form tag.
172 * @since 1.5.1
173 * @param string $form_url form action url
174 * @param string $name form name
175 * @param string $title form title
176 * @param string $button form button name
177 * @param array $defdata values of form fields
178 */
179 function abook_create_form($form_url,$name,$title,$button,$defdata=array()) {
180 global $color;
181 echo addForm($form_url, 'post', 'f_add').
182 html_tag( 'table',
183 html_tag( 'tr',
184 html_tag( 'td', "\n". '<strong>' . $title . '</strong>' . "\n",
185 'center', $color[0]
186 )
187 )
188 , 'center', '', 'width="90%"' ) ."\n";
189 address_form($name, $button, $defdata);
190 }
191
192
193 /**
194 * Had to move this function outside of the Addressbook Class
195 * PHP 4.0.4 Seemed to be having problems with inline functions.
196 * Note: this can return now since we don't support 4.0.4 anymore.
197 */
198 function addressbook_cmp($a,$b) {
199
200 if($a['backend'] > $b['backend']) {
201 return 1;
202 } else if($a['backend'] < $b['backend']) {
203 return -1;
204 }
205
206 return (strtolower($a['name']) > strtolower($b['name'])) ? 1 : -1;
207
208 }
209
210 /**
211 * Make an input field
212 * @param string $label
213 * @param string $field
214 * @param string $name
215 * @param string $size
216 * @param array $values
217 * @param string $add
218 */
219 function addressbook_inp_field($label, $field, $name, $size, $values, $add='') {
220 global $color;
221 $value = ( isset($values[$field]) ? $values[$field] : '');
222
223 if (is_array($value)) {
224 $td_str = addSelect($name.'['.$field.']', $value);
225 } else {
226 $td_str = addInput($name.'['.$field.']', $value, $size);
227 }
228 $td_str .= $add ;
229
230 return html_tag( 'tr' ,
231 html_tag( 'td', $label . ':', 'right', $color[4]) .
232 html_tag( 'td', $td_str, 'left', $color[4])
233 )
234 . "\n";
235 }
236
237 /**
238 * Output form to add and modify address data
239 */
240 function address_form($name, $submittext, $values = array()) {
241 global $color, $squirrelmail_language;
242
243 if ($squirrelmail_language == 'ja_JP') {
244 echo html_tag( 'table',
245 addressbook_inp_field(_("Nickname"), 'nickname', $name, 15, $values,
246 ' <small>' . _("Must be unique") . '</small>') .
247 addressbook_inp_field(_("E-mail address"), 'email', $name, 45, $values, '') .
248 addressbook_inp_field(_("Last name"), 'lastname', $name, 45, $values, '') .
249 addressbook_inp_field(_("First name"), 'firstname', $name, 45, $values, '') .
250 addressbook_inp_field(_("Additional info"), 'label', $name, 45, $values, '') .
251 list_writable_backends($name) .
252 html_tag( 'tr',
253 html_tag( 'td',
254 addSubmit($submittext, $name.'[SUBMIT]'),
255 'center', $color[4], 'colspan="2"')
256 )
257 , 'center', '', 'border="0" cellpadding="1" width="90%"') ."\n";
258 } else {
259 echo html_tag( 'table',
260 addressbook_inp_field(_("Nickname"), 'nickname', $name, 15, $values,
261 ' <small>' . _("Must be unique") . '</small>') .
262 addressbook_inp_field(_("E-mail address"), 'email', $name, 45, $values, '') .
263 addressbook_inp_field(_("First name"), 'firstname', $name, 45, $values, '') .
264 addressbook_inp_field(_("Last name"), 'lastname', $name, 45, $values, '') .
265 addressbook_inp_field(_("Additional info"), 'label', $name, 45, $values, '') .
266 list_writable_backends($name) .
267 html_tag( 'tr',
268 html_tag( 'td',
269 addSubmit($submittext, $name.'[SUBMIT]') ,
270 'center', $color[4], 'colspan="2"')
271 )
272 , 'center', '', 'border="0" cellpadding="1" width="90%"') ."\n";
273 }
274 }
275
276 /**
277 * Provides list of writeable backends.
278 * Works only when address is added ($name='addaddr')
279 * @param string $name name of form
280 * @return string html formated backend field (select or hidden)
281 */
282 function list_writable_backends($name) {
283 global $color, $abook;
284 if ( $name != 'addaddr' ) { return; }
285 $writeable_abook = 1;
286 if ( $abook->numbackends > 1 ) {
287 $backends = $abook->get_backend_list();
288 $writeable_abooks=array();
289 while (list($undef,$v) = each($backends)) {
290 if ($v->writeable) {
291 // add each backend to array
292 $writeable_abooks[$v->bnum]=$v->sname;
293 // save backend number
294 $writeable_abook=$v->bnum;
295 }
296 }
297 if (count($writeable_abooks)>1) {
298 // we have more than one writeable backend
299 $ret=addSelect('backend',$writeable_abooks,null,true);
300 return html_tag( 'tr',
301 html_tag( 'td', _("Add to:"),'right', $color[4] ) .
302 html_tag( 'td', $ret, 'left', $color[4] )) . "\n";
303 }
304 }
305 // Only one backend exists or is writeable.
306 return html_tag( 'tr',
307 html_tag( 'td',
308 addHidden('backend', $writeable_abook),
309 'center', $color[4], 'colspan="2"')) . "\n";
310 }
311
312 /**
313 * Sort array by the key "name"
314 */
315 function alistcmp($a,$b) {
316 $abook_sort_order=get_abook_sort();
317
318 switch ($abook_sort_order) {
319 case 0:
320 case 1:
321 $abook_sort='nickname';
322 break;
323 case 4:
324 case 5:
325 $abook_sort='email';
326 break;
327 case 6:
328 case 7:
329 $abook_sort='label';
330 break;
331 case 2:
332 case 3:
333 case 8:
334 default:
335 $abook_sort='name';
336 }
337
338 if ($a['backend'] > $b['backend']) {
339 return 1;
340 } else {
341 if ($a['backend'] < $b['backend']) {
342 return -1;
343 }
344 }
345
346 if( (($abook_sort_order+2) % 2) == 1) {
347 return (strtolower($a[$abook_sort]) < strtolower($b[$abook_sort])) ? 1 : -1;
348 } else {
349 return (strtolower($a[$abook_sort]) > strtolower($b[$abook_sort])) ? 1 : -1;
350 }
351 }
352
353 /**
354 * Address book sorting options
355 *
356 * returns address book sorting order
357 * @return integer book sorting options order
358 */
359 function get_abook_sort() {
360 global $data_dir, $username;
361
362 /* get sorting order */
363 if(sqgetGlobalVar('abook_sort_order', $temp, SQ_GET)) {
364 $abook_sort_order = (int) $temp;
365
366 if ($abook_sort_order < 0 or $abook_sort_order > 8)
367 $abook_sort_order=8;
368
369 setPref($data_dir, $username, 'abook_sort_order', $abook_sort_order);
370 } else {
371 /* get previous sorting options. default to unsorted */
372 $abook_sort_order = getPref($data_dir, $username, 'abook_sort_order', 8);
373 }
374
375 return $abook_sort_order;
376 }
377
378 /**
379 * This function shows the address book sort button.
380 *
381 * @param integer $abook_sort_order current sort value
382 * @param string $alt_tag alt tag value (string visible to text only browsers)
383 * @param integer $Down sort value when list is sorted ascending
384 * @param integer $Up sort value when list is sorted descending
385 * @return string html code with sorting images and urls
386 */
387 function show_abook_sort_button($abook_sort_order, $alt_tag, $Down, $Up ) {
388 global $form_url;
389
390 /* Figure out which image we want to use. */
391 if ($abook_sort_order != $Up && $abook_sort_order != $Down) {
392 $img = 'sort_none.png';
393 $which = $Up;
394 } elseif ($abook_sort_order == $Up) {
395 $img = 'up_pointer.png';
396 $which = $Down;
397 } else {
398 $img = 'down_pointer.png';
399 $which = 8;
400 }
401
402 /* Now that we have everything figured out, show the actual button. */
403 return ' <a href="' . $form_url .'?abook_sort_order=' . $which
404 . '"><img src="../images/' . $img
405 . '" border="0" width="12" height="10" alt="' . $alt_tag . '" title="'
406 . _("Click here to change the sorting of the address list") .'" /></a>';
407 }
408
409
410 /**
411 * This is the main address book class that connect all the
412 * backends and provide services to the functions above.
413 * @package squirrelmail
414 * @subpackage addressbook
415 */
416 class AddressBook {
417
418 /*
419 Cleaning errors from html with htmlspecialchars:
420 Errors from the backend are cleaned up in this class because we not always
421 have control over it when error output is generated in the backend.
422 If this appears to be wrong place then clean it up at the source (the backend)
423 */
424
425 /**
426 * Enabled address book backends
427 * @var array
428 */
429 var $backends = array();
430 /**
431 * Number of enabled backends
432 * @var integer
433 */
434 var $numbackends = 0;
435 /**
436 * Error messages
437 * @var string
438 */
439 var $error = '';
440 /**
441 * id of backend with personal address book
442 * @var integer
443 */
444 var $localbackend = 0;
445 /**
446 * Name of backend with personal address book
447 * @var string
448 */
449 var $localbackendname = '';
450
451 /**
452 * Constructor function.
453 */
454 function AddressBook() {
455 $this->localbackendname = _("Personal address book");
456 }
457
458 /**
459 * Return an array of backends of a given type,
460 * or all backends if no type is specified.
461 * @param string $type backend type
462 * @return array list of backends
463 */
464 function get_backend_list($type = '') {
465 $ret = array();
466 for ($i = 1 ; $i <= $this->numbackends ; $i++) {
467 if (empty($type) || $type == $this->backends[$i]->btype) {
468 $ret[] = &$this->backends[$i];
469 }
470 }
471 return $ret;
472 }
473
474
475 /* ========================== Public ======================== */
476
477 /**
478 * Add a new backend.
479 *
480 * @param string $backend backend name (without the abook_ prefix)
481 * @param mixed optional variable that is passed to the backend constructor.
482 * See each of the backend classes for valid parameters
483 * @return integer number of backends
484 */
485 function add_backend($backend, $param = '') {
486 $backend_name = 'abook_' . $backend;
487 eval('$newback = new ' . $backend_name . '($param);');
488 if(!empty($newback->error)) {
489 $this->error = $newback->error;
490 return false;
491 }
492
493 $this->numbackends++;
494
495 $newback->bnum = $this->numbackends;
496 $this->backends[$this->numbackends] = $newback;
497
498 /* Store ID of first local backend added */
499 if ($this->localbackend == 0 && $newback->btype == 'local') {
500 $this->localbackend = $this->numbackends;
501 $this->localbackendname = $newback->sname;
502 }
503
504 return $this->numbackends;
505 }
506
507
508 /**
509 * create string with name and email address
510 *
511 * This function takes a $row array as returned by the addressbook
512 * search and returns an e-mail address with the full name or
513 * nickname optionally prepended.
514 * @param array $row address book entry
515 * @return string email address with real name prepended
516 */
517 function full_address($row) {
518 global $addrsrch_fullname, $data_dir, $username;
519 $prefix = getPref($data_dir, $username, 'addrsrch_fullname');
520 if (($prefix != "" || (isset($addrsrch_fullname) &&
521 $prefix == $addrsrch_fullname)) && $prefix != 'noprefix') {
522 $name = ($prefix == 'nickname' ? $row['nickname'] : $row['name']);
523 return $name . ' <' . trim($row['email']) . '>';
524 } else {
525 return trim($row['email']);
526 }
527 }
528
529 /**
530 * Search for entries in address books
531 *
532 * Return a list of addresses matching expression in
533 * all backends of a given type.
534 * @param string $expression search expression
535 * @param integer $bnum backend number. default to search in all backends
536 * @return array search results
537 */
538 function search($expression, $bnum = -1) {
539 $ret = array();
540 $this->error = '';
541
542 /* Search all backends */
543 if ($bnum == -1) {
544 $sel = $this->get_backend_list('');
545 $failed = 0;
546 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
547 $backend = &$sel[$i];
548 $backend->error = '';
549 $res = $backend->search($expression);
550 if (is_array($res)) {
551 $ret = array_merge($ret, $res);
552 } else {
553 $this->error .= "<br />\n" . htmlspecialchars($backend->error);
554 $failed++;
555 }
556 }
557
558 /* Only fail if all backends failed */
559 if( $failed >= sizeof( $sel ) ) {
560 $ret = FALSE;
561 }
562
563 } else {
564
565 /* Search only one backend */
566
567 $ret = $this->backends[$bnum]->search($expression);
568 if (!is_array($ret)) {
569 $this->error .= "<br />\n" . htmlspecialchars($this->backends[$bnum]->error);
570 $ret = FALSE;
571 }
572 }
573
574 return( $ret );
575 }
576
577
578 /**
579 * Sorted search
580 * @param string $expression search expression
581 * @param integer $bnum backend number. default to search in all backends
582 * @return array search results
583 */
584 function s_search($expression, $bnum = -1) {
585
586 $ret = $this->search($expression, $bnum);
587 if ( is_array( $ret ) ) {
588 usort($ret, 'addressbook_cmp');
589 }
590 return $ret;
591 }
592
593
594 /**
595 * Lookup an address by alias.
596 * Only possible in local backends.
597 * @param string $alias
598 * @param integer backend number
599 * @return array lookup results. False, if not found.
600 */
601 function lookup($alias, $bnum = -1) {
602
603 $ret = array();
604
605 if ($bnum > -1) {
606 $res = $this->backends[$bnum]->lookup($alias);
607 if (is_array($res)) {
608 return $res;
609 } else {
610 $this->error = htmlspecialchars($backend->error);
611 return false;
612 }
613 }
614
615 $sel = $this->get_backend_list('local');
616 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
617 $backend = &$sel[$i];
618 $backend->error = '';
619 $res = $backend->lookup($alias);
620 if (is_array($res)) {
621 if(!empty($res))
622 return $res;
623 } else {
624 $this->error = htmlspecialchars($backend->error);
625 return false;
626 }
627 }
628
629 return $ret;
630 }
631
632
633 /**
634 * Return all addresses
635 * @param integer $bnum backend number
636 * @return array search results
637 */
638 function list_addr($bnum = -1) {
639 $ret = array();
640
641 if ($bnum == -1) {
642 $sel = $this->get_backend_list('');
643 } else {
644 $sel = array(0 => &$this->backends[$bnum]);
645 }
646
647 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
648 $backend = &$sel[$i];
649 $backend->error = '';
650 $res = $backend->list_addr();
651 if (is_array($res)) {
652 $ret = array_merge($ret, $res);
653 } else {
654 $this->error = htmlspecialchars($backend->error);
655 return false;
656 }
657 }
658
659 return $ret;
660 }
661
662 /**
663 * Create a new address
664 * @param array $userdata added address record
665 * @param integer $bnum backend number
666 * @return integer the backend number that the/ address was added
667 * to, or false if it failed.
668 */
669 function add($userdata, $bnum) {
670
671 /* Validate data */
672 if (!is_array($userdata)) {
673 $this->error = _("Invalid input data");
674 return false;
675 }
676 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
677 $this->error = _("Name is missing");
678 return false;
679 }
680 if (empty($userdata['email'])) {
681 $this->error = _("E-mail address is missing");
682 return false;
683 }
684 if (empty($userdata['nickname'])) {
685 $userdata['nickname'] = $userdata['email'];
686 }
687
688 if (eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
689 $this->error = _("Nickname contains illegal characters");
690 return false;
691 }
692
693 /* Check that specified backend accept new entries */
694 if (!$this->backends[$bnum]->writeable) {
695 $this->error = _("Addressbook is read-only");
696 return false;
697 }
698
699 /* Add address to backend */
700 $res = $this->backends[$bnum]->add($userdata);
701 if ($res) {
702 return $bnum;
703 } else {
704 $this->error = htmlspecialchars($this->backends[$bnum]->error);
705 return false;
706 }
707
708 return false; // Not reached
709 } /* end of add() */
710
711
712 /**
713 * Remove the entries from address book
714 * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
715 * @param integer $bnum backend number
716 * @return bool true if removed successfully. false if there s an error. $this->error contains error message
717 */
718 function remove($alias, $bnum) {
719
720 /* Check input */
721 if (empty($alias)) {
722 return true;
723 }
724
725 /* Convert string to single element array */
726 if (!is_array($alias)) {
727 $alias = array(0 => $alias);
728 }
729
730 /* Check that specified backend is writable */
731 if (!$this->backends[$bnum]->writeable) {
732 $this->error = _("Addressbook is read-only");
733 return false;
734 }
735
736 /* Remove user from backend */
737 $res = $this->backends[$bnum]->remove($alias);
738 if ($res) {
739 return $bnum;
740 } else {
741 $this->error = htmlspecialchars($this->backends[$bnum]->error);
742 return false;
743 }
744
745 return FALSE; /* Not reached */
746 } /* end of remove() */
747
748
749 /**
750 * Modify entry in address book
751 * @param string $alias nickname
752 * @param array $userdata newdata
753 * @param integer $bnum backend number
754 */
755 function modify($alias, $userdata, $bnum) {
756
757 /* Check input */
758 if (empty($alias) || !is_string($alias)) {
759 return true;
760 }
761
762 /* Validate data */
763 if(!is_array($userdata)) {
764 $this->error = _("Invalid input data");
765 return false;
766 }
767 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
768 $this->error = _("Name is missing");
769 return false;
770 }
771 if (empty($userdata['email'])) {
772 $this->error = _("E-mail address is missing");
773 return false;
774 }
775
776 if (eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
777 $this->error = _("Nickname contains illegal characters");
778 return false;
779 }
780
781 if (empty($userdata['nickname'])) {
782 $userdata['nickname'] = $userdata['email'];
783 }
784
785 /* Check that specified backend is writable */
786 if (!$this->backends[$bnum]->writeable) {
787 $this->error = _("Addressbook is read-only");;
788 return false;
789 }
790
791 /* Modify user in backend */
792 $res = $this->backends[$bnum]->modify($alias, $userdata);
793 if ($res) {
794 return $bnum;
795 } else {
796 $this->error = htmlspecialchars($this->backends[$bnum]->error);
797 return false;
798 }
799
800 return FALSE; /* Not reached */
801 } /* end of modify() */
802
803
804 } /* End of class Addressbook */
805
806 /**
807 * Generic backend that all other backends extend
808 * @package squirrelmail
809 * @subpackage addressbook
810 */
811 class addressbook_backend {
812
813 /* Variables that all backends must provide. */
814 /**
815 * Backend type
816 *
817 * Can be 'local' or 'remote'
818 * @var string backend type
819 */
820 var $btype = 'dummy';
821 /**
822 * Internal backend name
823 * @var string
824 */
825 var $bname = 'dummy';
826 /**
827 * Displayed backend name
828 * @var string
829 */
830 var $sname = 'Dummy backend';
831
832 /*
833 * Variables common for all backends, but that
834 * should not be changed by the backends.
835 */
836 /**
837 * Backend number
838 * @var integer
839 */
840 var $bnum = -1;
841 /**
842 * Error messages
843 * @var string
844 */
845 var $error = '';
846 /**
847 * Writeable flag
848 * @var bool
849 */
850 var $writeable = false;
851
852 /**
853 * Set error message
854 * @param string $string error message
855 * @return bool
856 */
857 function set_error($string) {
858 $this->error = '[' . $this->sname . '] ' . $string;
859 return false;
860 }
861
862
863 /* ========================== Public ======================== */
864
865 /**
866 * Search for entries in backend
867 *
868 * Working backend should support use of wildcards. * symbol
869 * should match one or more symbols. ? symbol should match any
870 * single symbol.
871 * @param string $expression
872 * @return bool
873 */
874 function search($expression) {
875 $this->set_error('search not implemented');
876 return false;
877 }
878
879 /**
880 * Find entry in backend by alias
881 * @param string $alias name used for id
882 * @return bool
883 */
884 function lookup($alias) {
885 $this->set_error('lookup not implemented');
886 return false;
887 }
888
889 /**
890 * List all entries in backend
891 *
892 * Working backend should provide this function or at least
893 * dummy function that returns empty array.
894 * @return bool
895 */
896 function list_addr() {
897 $this->set_error('list_addr not implemented');
898 return false;
899 }
900
901 /**
902 * Add entry to backend
903 * @param array userdata
904 * @return bool
905 */
906 function add($userdata) {
907 $this->set_error('add not implemented');
908 return false;
909 }
910
911 /**
912 * Remove entry from backend
913 * @param string $alias name used for id
914 * @return bool
915 */
916 function remove($alias) {
917 $this->set_error('delete not implemented');
918 return false;
919 }
920
921 /**
922 * Modify entry in backend
923 * @param string $alias name used for id
924 * @param array $newuserdata new data
925 * @return bool
926 */
927 function modify($alias, $newuserdata) {
928 $this->set_error('modify not implemented');
929 return false;
930 }
931 }
932
933 /*
934 PHP 5 requires that the class be made first, which seems rather
935 logical, and should have been the way it was generated the first time.
936 */
937
938 require_once(SM_PATH . 'functions/abook_local_file.php');
939 require_once(SM_PATH . 'functions/abook_ldap_server.php');
940
941 /* Only load database backend if database is configured */
942 if((isset($addrbook_dsn) && !empty($addrbook_dsn)) ||
943 (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn))) {
944 include_once(SM_PATH . 'functions/abook_database.php');
945 }
946
947 /*
948 * hook allows adding different address book classes.
949 * class must follow address book class coding standards.
950 *
951 * see addressbook_backend class and functions/abook_*.php files.
952 * @since 1.5.1 and 1.4.5
953 */
954 do_hook('abook_add_class');
955
956 ?>