Reverting to old string.
[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-2006 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 /* required includes */
24 include_once(SM_PATH . 'functions/display_messages.php');
25 include_once(SM_PATH . 'templates/util_global.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 * @param string $icon_theme_path Path to user's current icon theme
387 * @return string html code with sorting images and urls
388 */
389 function show_abook_sort_button($abook_sort_order, $alt_tag, $Down, $Up ) {
390 global $form_url, $icon_theme_path;
391
392 /* Figure out which image we want to use. */
393 if ($abook_sort_order != $Up && $abook_sort_order != $Down) {
394 $img = 'sort_none.png';
395 $which = $Up;
396 } elseif ($abook_sort_order == $Up) {
397 $img = 'up_pointer.png';
398 $which = $Down;
399 } else {
400 $img = 'down_pointer.png';
401 $which = 8;
402 }
403
404 /* Now that we have everything figured out, show the actual button. */
405 return ' <a href="' . $form_url .'?abook_sort_order=' . $which .
406 '">' .
407 getIcon($icon_theme_path, $img, $alt_tag, _("Click here to change the sorting of the address list")) .
408 '</a>';
409 }
410
411
412 /**
413 * This is the main address book class that connect all the
414 * backends and provide services to the functions above.
415 * @package squirrelmail
416 * @subpackage addressbook
417 */
418 class AddressBook {
419
420 /*
421 Cleaning errors from html with htmlspecialchars:
422 Errors from the backend are cleaned up in this class because we not always
423 have control over it when error output is generated in the backend.
424 If this appears to be wrong place then clean it up at the source (the backend)
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 * Controls use of 'extra' field
454 *
455 * Extra field can be used to add link to form, which allows
456 * to modify all fields supported by backend. This is the only field
457 * that is not sanitized with htmlspecialchars. Backends MUST make
458 * sure that field data is sanitized and displayed correctly inside
459 * table cell. Use of html formating in other address book fields is
460 * not allowed. Backends that don't return 'extra' row in address book
461 * data should not modify this object property.
462 * @var boolean
463 * @since 1.5.1
464 */
465 var $add_extra_field = false;
466
467 /**
468 * Constructor function.
469 */
470 function AddressBook() {
471 $this->localbackendname = _("Personal address book");
472 }
473
474 /**
475 * Return an array of backends of a given type,
476 * or all backends if no type is specified.
477 * @param string $type backend type
478 * @return array list of backends
479 */
480 function get_backend_list($type = '') {
481 $ret = array();
482 for ($i = 1 ; $i <= $this->numbackends ; $i++) {
483 if (empty($type) || $type == $this->backends[$i]->btype) {
484 $ret[] = &$this->backends[$i];
485 }
486 }
487 return $ret;
488 }
489
490
491 /* ========================== Public ======================== */
492
493 /**
494 * Add a new backend.
495 *
496 * @param string $backend backend name (without the abook_ prefix)
497 * @param mixed optional variable that is passed to the backend constructor.
498 * See each of the backend classes for valid parameters
499 * @return integer number of backends
500 */
501 function add_backend($backend, $param = '') {
502 $backend_name = 'abook_' . $backend;
503 eval('$newback = new ' . $backend_name . '($param);');
504 if(!empty($newback->error)) {
505 $this->error = $newback->error;
506 return false;
507 }
508
509 $this->numbackends++;
510
511 $newback->bnum = $this->numbackends;
512 $this->backends[$this->numbackends] = $newback;
513
514 /* Store ID of first local backend added */
515 if ($this->localbackend == 0 && $newback->btype == 'local') {
516 $this->localbackend = $this->numbackends;
517 $this->localbackendname = $newback->sname;
518 }
519
520 return $this->numbackends;
521 }
522
523
524 /**
525 * create string with name and email address
526 *
527 * This function takes a $row array as returned by the addressbook
528 * search and returns an e-mail address with the full name or
529 * nickname optionally prepended.
530 * @param array $row address book entry
531 * @return string email address with real name prepended
532 */
533 function full_address($row) {
534 global $addrsrch_fullname, $data_dir, $username;
535 $prefix = getPref($data_dir, $username, 'addrsrch_fullname');
536 if (($prefix != "" || (isset($addrsrch_fullname) &&
537 $prefix == $addrsrch_fullname)) && $prefix != 'noprefix') {
538 $name = ($prefix == 'nickname' ? $row['nickname'] : $row['name']);
539 return $name . ' <' . trim($row['email']) . '>';
540 } else {
541 return trim($row['email']);
542 }
543 }
544
545 /**
546 * Search for entries in address books
547 *
548 * Return a list of addresses matching expression in
549 * all backends of a given type.
550 * @param string $expression search expression
551 * @param integer $bnum backend number. default to search in all backends
552 * @return array search results
553 */
554 function search($expression, $bnum = -1) {
555 $ret = array();
556 $this->error = '';
557
558 /* Search all backends */
559 if ($bnum == -1) {
560 $sel = $this->get_backend_list('');
561 $failed = 0;
562 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
563 $backend = &$sel[$i];
564 $backend->error = '';
565 $res = $backend->search($expression);
566 if (is_array($res)) {
567 $ret = array_merge($ret, $res);
568 } else {
569 $this->error .= "<br />\n" . htmlspecialchars($backend->error);
570 $failed++;
571 }
572 }
573
574 /* Only fail if all backends failed */
575 if( $failed >= sizeof( $sel ) ) {
576 $ret = FALSE;
577 }
578
579 } else {
580
581 /* Search only one backend */
582
583 $ret = $this->backends[$bnum]->search($expression);
584 if (!is_array($ret)) {
585 $this->error .= "<br />\n" . htmlspecialchars($this->backends[$bnum]->error);
586 $ret = FALSE;
587 }
588 }
589
590 return( $ret );
591 }
592
593
594 /**
595 * Sorted search
596 * @param string $expression search expression
597 * @param integer $bnum backend number. default to search in all backends
598 * @return array search results
599 */
600 function s_search($expression, $bnum = -1) {
601
602 $ret = $this->search($expression, $bnum);
603 if ( is_array( $ret ) ) {
604 usort($ret, 'addressbook_cmp');
605 }
606 return $ret;
607 }
608
609
610 /**
611 * Lookup an address by alias.
612 * Only possible in local backends.
613 * @param string $alias
614 * @param integer backend number
615 * @return array lookup results. False, if not found.
616 */
617 function lookup($alias, $bnum = -1) {
618
619 $ret = array();
620
621 if ($bnum > -1) {
622 $res = $this->backends[$bnum]->lookup($alias);
623 if (is_array($res)) {
624 return $res;
625 } else {
626 $this->error = htmlspecialchars($this->backends[$bnum]->error);
627 return false;
628 }
629 }
630
631 $sel = $this->get_backend_list('local');
632 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
633 $backend = &$sel[$i];
634 $backend->error = '';
635 $res = $backend->lookup($alias);
636 if (is_array($res)) {
637 if(!empty($res))
638 return $res;
639 } else {
640 $this->error = htmlspecialchars($backend->error);
641 return false;
642 }
643 }
644
645 return $ret;
646 }
647
648
649 /**
650 * Return all addresses
651 * @param integer $bnum backend number
652 * @return array search results
653 */
654 function list_addr($bnum = -1) {
655 $ret = array();
656
657 if ($bnum == -1) {
658 $sel = $this->get_backend_list('');
659 } else {
660 $sel = array(0 => &$this->backends[$bnum]);
661 }
662
663 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
664 $backend = &$sel[$i];
665 $backend->error = '';
666 $res = $backend->list_addr();
667 if (is_array($res)) {
668 $ret = array_merge($ret, $res);
669 } else {
670 $this->error = htmlspecialchars($backend->error);
671 return false;
672 }
673 }
674
675 return $ret;
676 }
677
678 /**
679 * Create a new address
680 * @param array $userdata added address record
681 * @param integer $bnum backend number
682 * @return integer the backend number that the/ address was added
683 * to, or false if it failed.
684 */
685 function add($userdata, $bnum) {
686
687 /* Validate data */
688 if (!is_array($userdata)) {
689 $this->error = _("Invalid input data");
690 return false;
691 }
692 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
693 $this->error = _("Name is missing");
694 return false;
695 }
696 if (empty($userdata['email'])) {
697 $this->error = _("E-mail address is missing");
698 return false;
699 }
700 if (empty($userdata['nickname'])) {
701 $userdata['nickname'] = $userdata['email'];
702 }
703
704 if (eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
705 $this->error = _("Nickname contains illegal characters");
706 return false;
707 }
708
709 /* Check that specified backend accept new entries */
710 if (!$this->backends[$bnum]->writeable) {
711 $this->error = _("Addressbook is read-only");
712 return false;
713 }
714
715 /* Add address to backend */
716 $res = $this->backends[$bnum]->add($userdata);
717 if ($res) {
718 return $bnum;
719 } else {
720 $this->error = htmlspecialchars($this->backends[$bnum]->error);
721 return false;
722 }
723
724 return false; // Not reached
725 } /* end of add() */
726
727
728 /**
729 * Remove the entries from address book
730 * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
731 * @param integer $bnum backend number
732 * @return bool true if removed successfully. false if there s an error. $this->error contains error message
733 */
734 function remove($alias, $bnum) {
735
736 /* Check input */
737 if (empty($alias)) {
738 return true;
739 }
740
741 /* Convert string to single element array */
742 if (!is_array($alias)) {
743 $alias = array(0 => $alias);
744 }
745
746 /* Check that specified backend is writable */
747 if (!$this->backends[$bnum]->writeable) {
748 $this->error = _("Addressbook is read-only");
749 return false;
750 }
751
752 /* Remove user from backend */
753 $res = $this->backends[$bnum]->remove($alias);
754 if ($res) {
755 return $bnum;
756 } else {
757 $this->error = htmlspecialchars($this->backends[$bnum]->error);
758 return false;
759 }
760
761 return FALSE; /* Not reached */
762 } /* end of remove() */
763
764
765 /**
766 * Modify entry in address book
767 * @param string $alias nickname
768 * @param array $userdata newdata
769 * @param integer $bnum backend number
770 */
771 function modify($alias, $userdata, $bnum) {
772
773 /* Check input */
774 if (empty($alias) || !is_string($alias)) {
775 return true;
776 }
777
778 /* Validate data */
779 if(!is_array($userdata)) {
780 $this->error = _("Invalid input data");
781 return false;
782 }
783 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
784 $this->error = _("Name is missing");
785 return false;
786 }
787 if (empty($userdata['email'])) {
788 $this->error = _("E-mail address is missing");
789 return false;
790 }
791
792 if (eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
793 $this->error = _("Nickname contains illegal characters");
794 return false;
795 }
796
797 if (empty($userdata['nickname'])) {
798 $userdata['nickname'] = $userdata['email'];
799 }
800
801 /* Check that specified backend is writable */
802 if (!$this->backends[$bnum]->writeable) {
803 $this->error = _("Addressbook is read-only");;
804 return false;
805 }
806
807 /* Modify user in backend */
808 $res = $this->backends[$bnum]->modify($alias, $userdata);
809 if ($res) {
810 return $bnum;
811 } else {
812 $this->error = htmlspecialchars($this->backends[$bnum]->error);
813 return false;
814 }
815
816 return FALSE; /* Not reached */
817 } /* end of modify() */
818
819
820 } /* End of class Addressbook */
821
822 /**
823 * Generic backend that all other backends extend
824 * @package squirrelmail
825 * @subpackage addressbook
826 */
827 class addressbook_backend {
828
829 /* Variables that all backends must provide. */
830 /**
831 * Backend type
832 *
833 * Can be 'local' or 'remote'
834 * @var string backend type
835 */
836 var $btype = 'dummy';
837 /**
838 * Internal backend name
839 * @var string
840 */
841 var $bname = 'dummy';
842 /**
843 * Displayed backend name
844 * @var string
845 */
846 var $sname = 'Dummy backend';
847
848 /*
849 * Variables common for all backends, but that
850 * should not be changed by the backends.
851 */
852 /**
853 * Backend number
854 * @var integer
855 */
856 var $bnum = -1;
857 /**
858 * Error messages
859 * @var string
860 */
861 var $error = '';
862 /**
863 * Writeable flag
864 * @var bool
865 */
866 var $writeable = false;
867
868 /**
869 * Set error message
870 * @param string $string error message
871 * @return bool
872 */
873 function set_error($string) {
874 $this->error = '[' . $this->sname . '] ' . $string;
875 return false;
876 }
877
878
879 /* ========================== Public ======================== */
880
881 /**
882 * Search for entries in backend
883 *
884 * Working backend should support use of wildcards. * symbol
885 * should match one or more symbols. ? symbol should match any
886 * single symbol.
887 * @param string $expression
888 * @return bool
889 */
890 function search($expression) {
891 $this->set_error('search not implemented');
892 return false;
893 }
894
895 /**
896 * Find entry in backend by alias
897 * @param string $alias name used for id
898 * @return bool
899 */
900 function lookup($alias) {
901 $this->set_error('lookup not implemented');
902 return false;
903 }
904
905 /**
906 * List all entries in backend
907 *
908 * Working backend should provide this function or at least
909 * dummy function that returns empty array.
910 * @return bool
911 */
912 function list_addr() {
913 $this->set_error('list_addr not implemented');
914 return false;
915 }
916
917 /**
918 * Add entry to backend
919 * @param array userdata
920 * @return bool
921 */
922 function add($userdata) {
923 $this->set_error('add not implemented');
924 return false;
925 }
926
927 /**
928 * Remove entry from backend
929 * @param string $alias name used for id
930 * @return bool
931 */
932 function remove($alias) {
933 $this->set_error('delete not implemented');
934 return false;
935 }
936
937 /**
938 * Modify entry in backend
939 * @param string $alias name used for id
940 * @param array $newuserdata new data
941 * @return bool
942 */
943 function modify($alias, $newuserdata) {
944 $this->set_error('modify not implemented');
945 return false;
946 }
947 }
948
949 /*
950 PHP 5 requires that the class be made first, which seems rather
951 logical, and should have been the way it was generated the first time.
952 */
953
954 require_once(SM_PATH . 'functions/abook_local_file.php');
955 require_once(SM_PATH . 'functions/abook_ldap_server.php');
956
957 /* Only load database backend if database is configured */
958 if((isset($addrbook_dsn) && !empty($addrbook_dsn)) ||
959 (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn))) {
960 include_once(SM_PATH . 'functions/abook_database.php');
961 }
962
963 /*
964 * hook allows adding different address book classes.
965 * class must follow address book class coding standards.
966 *
967 * see addressbook_backend class and functions/abook_*.php files.
968 * @since 1.5.1 and 1.4.5
969 */
970 do_hook('abook_add_class');
971
972 ?>