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