resubmitting last Monday updates and adding php 5.1.0 timezone support
[squirrelmail.git] / functions / addressbook.php
CommitLineData
5100704d 1<?php
7390e240 2
35586184 3/**
6ad2bbe2 4 * functions/addressbook.php - Functions and classes for the addressbook system
35586184 5 *
6ad2bbe2 6 * Functions require SM_PATH and support of forms.php functions
35586184 7 *
47ccfad4 8 * @copyright &copy; 1999-2006 The SquirrelMail Project Team
4b4abf93 9 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
a9d318b0 10 * @version $Id$
d6c32258 11 * @package squirrelmail
a9d318b0 12 * @subpackage addressbook
35586184 13 */
14
867fed37 15/** required includes */
202bcbcc 16// FIXME, NO display code in functions files
635f7200 17include_once(SM_PATH . 'templates/util_global.php');
04875ae0 18
d6c32258 19/**
0d4096aa 20 * Create and initialize an addressbook object.
f8a1ed5a 21 * @param boolean $showerr display any address book init errors. html page header
0d4096aa 22 * must be created before calling addressbook_init() with $showerr enabled.
867fed37 23 * @param boolean $onlylocal enable only local address book backends. Should
24 * be used when code does not need access to remote backends. Backends
25 * that provide read only address books with limited listing options can be
26 * tagged as remote.
0d4096aa 27 * @return object address book object.
28 */
81fa4801 29function addressbook_init($showerr = true, $onlylocal = false) {
04875ae0 30 global $data_dir, $username, $color, $ldap_server, $address_book_global_filename;
81fa4801 31 global $addrbook_dsn, $addrbook_table;
e59a9c41 32 global $abook_global_file, $abook_global_file_writeable, $abook_global_file_listing;
30e9932c 33 global $addrbook_global_dsn, $addrbook_global_table, $addrbook_global_writeable, $addrbook_global_listing;
81fa4801 34
35 /* Create a new addressbook object */
36 $abook = new AddressBook;
37
04875ae0 38 /* Create empty error message */
39 $abook_init_error='';
40
81fa4801 41 /*
42 Always add a local backend. We use *either* file-based *or* a
43 database addressbook. If $addrbook_dsn is set, the database
44 backend is used. If not, addressbooks are stores in files.
45 */
46 if (isset($addrbook_dsn) && !empty($addrbook_dsn)) {
47 /* Database */
48 if (!isset($addrbook_table) || empty($addrbook_table)) {
49 $addrbook_table = 'address';
50 }
51 $r = $abook->add_backend('database', Array('dsn' => $addrbook_dsn,
52 'owner' => $username,
53 'table' => $addrbook_table));
54 if (!$r && $showerr) {
0d4096aa 55 $abook_init_error.=_("Error initializing addressbook database.") . "<br />\n" . $abook->error;
81fa4801 56 }
57 } else {
58 /* File */
59 $filename = getHashedFile($username, $data_dir, "$username.abook");
60 $r = $abook->add_backend('local_file', Array('filename' => $filename,
61 'create' => true));
62 if(!$r && $showerr) {
0d4096aa 63 // no need to use $abook->error, because message explains error.
64 $abook_init_error.=sprintf( _("Error opening file %s"), $filename );
81fa4801 65 }
81fa4801 66 }
67
e59a9c41 68 /* Global file based addressbook */
f8a1ed5a 69 if (isset($abook_global_file) &&
e59a9c41 70 isset($abook_global_file_writeable) &&
71 isset($abook_global_file_listing) &&
72 trim($abook_global_file)!=''){
73
4272758c 74 // Detect place of address book
75 if (! preg_match("/[\/\\\]/",$abook_global_file)) {
e4a468a7 76 /* no path chars, address book stored in data directory
f8a1ed5a 77 * make sure that there is a slash between data directory
e4a468a7 78 * and address book file name
79 */
80 $abook_global_filename=$data_dir
81 . ((substr($data_dir, -1) != '/') ? '/' : '')
82 . $abook_global_file;
4272758c 83 } elseif (preg_match("/^\/|\w:/",$abook_global_file)) {
84 // full path is set in options (starts with slash or x:)
85 $abook_global_filename=$abook_global_file;
86 } else {
87 $abook_global_filename=SM_PATH . $abook_global_file;
88 }
e59a9c41 89
4272758c 90 $r = $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
91 'name' => _("Global address book"),
92 'detect_writeable' => false,
e59a9c41 93 'writeable'=> $abook_global_file_writeable,
94 'listing' => $abook_global_file_listing));
04875ae0 95
96 /* global abook init error is not fatal. add error message and continue */
81fa4801 97 if (!$r && $showerr) {
0d4096aa 98 if ($abook_init_error!='') $abook_init_error.="<br />\n";
99 $abook_init_error.=_("Error initializing global addressbook.") . "<br />\n" . $abook->error;
81fa4801 100 }
101 }
102
30e9932c 103 /* Load global addressbook from SQL if configured */
104 if (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn)) {
105 /* Database configured */
106 if (!isset($addrbook_global_table) || empty($addrbook_global_table)) {
c1ac62d4 107 $addrbook_global_table = 'global_abook';
30e9932c 108 }
109 $r = $abook->add_backend('database',
c1ac62d4 110 Array('dsn' => $addrbook_global_dsn,
111 'owner' => 'global',
112 'name' => _("Global address book"),
113 'writeable' => $addrbook_global_writeable,
114 'listing' => $addrbook_global_listing,
115 'table' => $addrbook_global_table));
0d4096aa 116 /* global abook init error is not fatal. add error message and continue */
117 if (!$r && $showerr) {
118 if ($abook_init_error!='') $abook_init_error.="<br />\n";
119 $abook_init_error.=_("Error initializing global addressbook.") . "<br />\n" . $abook->error;
120 }
30e9932c 121 }
122
df788686 123 /*
124 * hook allows to include different address book backends.
125 * plugins should extract $abook and $r from arguments
126 * and use same add_backend commands as above functions.
867fed37 127 * Since 1.5.2 hook sends third ($onlylocal) argument to address book
128 * plugins in order to allow detection of local address book init.
7390e240 129 * @since 1.5.1 and 1.4.5
df788686 130 */
867fed37 131 $hookReturn = do_hook('abook_init', $abook, $r, $onlylocal);
df788686 132 $abook = $hookReturn[1];
133 $r = $hookReturn[2];
867fed37 134 if (!$r && $showerr) {
135 if ($abook_init_error!='') $abook_init_error.="<br />\n";
136 $abook_init_error.=_("Error initializing other address books.") . "<br />\n" . $abook->error;
137 }
62f7daa5 138
0d4096aa 139 if (! $onlylocal) {
140 /* Load configured LDAP servers (if PHP has LDAP support) */
95501c13 141 if (isset($ldap_server) && is_array($ldap_server)) {
0d4096aa 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:") .
7390e240 149 "<br />\n", $param['host']);
0d4096aa 150 $abook_init_error.= $abook->error;
151 }
81fa4801 152 }
153 }
0d4096aa 154 } // end of ldap server init
155 } // end of remote abook backend init
4935919f 156
04875ae0 157 /**
158 * display address book init errors.
159 */
160 if ($abook_init_error!='' && $showerr) {
161 error_box($abook_init_error,$color);
162 }
7390e240 163
81fa4801 164 /* Return the initialized object */
165 return $abook;
4935919f 166}
167
c1ac62d4 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 */
179function 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 )
81642286 188 , 'center', '', 'width="90%"' ) ."\n";
c1ac62d4 189 address_form($name, $button, $defdata);
190}
191
4935919f 192
327e2d96 193/**
81fa4801 194 * Had to move this function outside of the Addressbook Class
195 * PHP 4.0.4 Seemed to be having problems with inline functions.
abd74f7d 196 * Note: this can return now since we don't support 4.0.4 anymore.
62f7daa5 197 */
81fa4801 198function addressbook_cmp($a,$b) {
4935919f 199
81fa4801 200 if($a['backend'] > $b['backend']) {
201 return 1;
202 } else if($a['backend'] < $b['backend']) {
203 return -1;
204 }
62f7daa5 205
81fa4801 206 return (strtolower($a['name']) > strtolower($b['name'])) ? 1 : -1;
4935919f 207
81fa4801 208}
4935919f 209
c1ac62d4 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 */
219function 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 */
240function 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
6ad2bbe2 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 */
c1ac62d4 282function list_writable_backends($name) {
283 global $color, $abook;
284 if ( $name != 'addaddr' ) { return; }
6ad2bbe2 285 $writeable_abook = 1;
c1ac62d4 286 if ( $abook->numbackends > 1 ) {
c1ac62d4 287 $backends = $abook->get_backend_list();
6ad2bbe2 288 $writeable_abooks=array();
c1ac62d4 289 while (list($undef,$v) = each($backends)) {
290 if ($v->writeable) {
6ad2bbe2 291 // add each backend to array
292 $writeable_abooks[$v->bnum]=$v->sname;
293 // save backend number
294 $writeable_abook=$v->bnum;
c1ac62d4 295 }
296 }
6ad2bbe2 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 }
c1ac62d4 304 }
6ad2bbe2 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";
c1ac62d4 310}
311
312/**
313 * Sort array by the key "name"
314 */
315function 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 */
359function 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 */
387function show_abook_sort_button($abook_sort_order, $alt_tag, $Down, $Up ) {
635f7200 388 global $form_url, $icon_theme_path;
c1ac62d4 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';
de783fdf 393 $text_icon = '&#9723;'; // U+25FB WHITE MEDIUM SQUARE
c1ac62d4 394 $which = $Up;
395 } elseif ($abook_sort_order == $Up) {
396 $img = 'up_pointer.png';
de783fdf 397 $text_icon = '&#8679;'; // U+21E7 UPWARDS WHITE ARROW
c1ac62d4 398 $which = $Down;
399 } else {
400 $img = 'down_pointer.png';
de783fdf 401 $text_icon = '&#8681;'; // U+21E9 DOWNWARDS WHITE ARROW
c1ac62d4 402 $which = 8;
403 }
404
635f7200 405 /* Now that we have everything figured out, show the actual button. */
de783fdf 406 return '&nbsp;<a href="' . $form_url .'?abook_sort_order=' . $which .
407 '" style="text-decoration:none" title="'.$alt_tag.'">' .
408 getIcon($icon_theme_path, $img, $text_icon, $alt_tag) .
635f7200 409 '</a>';
c1ac62d4 410}
411
4935919f 412
8f6f9ba5 413/**
81fa4801 414 * This is the main address book class that connect all the
415 * backends and provide services to the functions above.
8f6f9ba5 416 * @package squirrelmail
c1ac62d4 417 * @subpackage addressbook
81fa4801 418 */
81fa4801 419class AddressBook {
0493ed11 420
421 /*
422 Cleaning errors from html with htmlspecialchars:
423 Errors from the backend are cleaned up in this class because we not always
424 have control over it when error output is generated in the backend.
425 If this appears to be wrong place then clean it up at the source (the backend)
0493ed11 426 */
427
4272758c 428 /**
429 * Enabled address book backends
430 * @var array
431 */
81fa4801 432 var $backends = array();
4272758c 433 /**
434 * Number of enabled backends
435 * @var integer
436 */
81fa4801 437 var $numbackends = 0;
4272758c 438 /**
439 * Error messages
440 * @var string
441 */
81fa4801 442 var $error = '';
4272758c 443 /**
444 * id of backend with personal address book
445 * @var integer
446 */
81fa4801 447 var $localbackend = 0;
4272758c 448 /**
449 * Name of backend with personal address book
450 * @var string
451 */
81fa4801 452 var $localbackendname = '';
7b0ea860 453 /**
454 * Controls use of 'extra' field
202bcbcc 455 *
456 * Extra field can be used to add link to form, which allows
457 * to modify all fields supported by backend. This is the only field
7b0ea860 458 * that is not sanitized with htmlspecialchars. Backends MUST make
459 * sure that field data is sanitized and displayed correctly inside
460 * table cell. Use of html formating in other address book fields is
202bcbcc 461 * not allowed. Backends that don't return 'extra' row in address book
7b0ea860 462 * data should not modify this object property.
463 * @var boolean
464 * @since 1.5.1
465 */
466 var $add_extra_field = false;
62f7daa5 467
4272758c 468 /**
469 * Constructor function.
470 */
81fa4801 471 function AddressBook() {
c6b8b46c 472 $this->localbackendname = _("Personal address book");
81fa4801 473 }
4935919f 474
4272758c 475 /**
81fa4801 476 * Return an array of backends of a given type,
477 * or all backends if no type is specified.
4272758c 478 * @param string $type backend type
479 * @return array list of backends
81fa4801 480 */
481 function get_backend_list($type = '') {
482 $ret = array();
483 for ($i = 1 ; $i <= $this->numbackends ; $i++) {
484 if (empty($type) || $type == $this->backends[$i]->btype) {
485 $ret[] = &$this->backends[$i];
486 }
4935919f 487 }
81fa4801 488 return $ret;
489 }
4935919f 490
491
4272758c 492 /* ========================== Public ======================== */
81fa4801 493
4272758c 494 /**
495 * Add a new backend.
496 *
497 * @param string $backend backend name (without the abook_ prefix)
498 * @param mixed optional variable that is passed to the backend constructor.
499 * See each of the backend classes for valid parameters
500 * @return integer number of backends
81fa4801 501 */
502 function add_backend($backend, $param = '') {
202bcbcc 503 static $backend_classes;
504 if (!isset($backend_classes)) {
505 $backend_classes = array();
506 }
507 if (!isset($backend_classes[$backend])) {
508 /**
509 * Support backend provided by plugins. Plugin function must
510 * return an associative array with as key the backend name ($backend)
511 * and as value the file including the path containing the backend class.
512 * i.e.: $aBackend = array('backend_template' => SM_PATH . 'plugins/abook_backend_template/functions.php')
513 *
514 * NB: Because the backend files are included from within this function they DO NOT have access to
515 * vars in the global scope. This function is the global scope for the included backend !!!
516 */
517 $aBackend = do_hook('abook_add_class');
518 if (isset($aBackend) && is_array($aBackend) && isset($aBackend[$backend])) {
519 require_once($aBackend[$backend]);
520 } else {
521 require_once(SM_PATH . 'functions/abook_'.$backend.'.php');
522 }
523 $backend_classes[$backend] = true;
524 }
81fa4801 525 $backend_name = 'abook_' . $backend;
202bcbcc 526 $newback = new $backend_name($param);
527 //eval('$newback = new ' . $backend_name . '($param);');
81fa4801 528 if(!empty($newback->error)) {
529 $this->error = $newback->error;
530 return false;
531 }
532
533 $this->numbackends++;
534
535 $newback->bnum = $this->numbackends;
536 $this->backends[$this->numbackends] = $newback;
62f7daa5 537
81fa4801 538 /* Store ID of first local backend added */
539 if ($this->localbackend == 0 && $newback->btype == 'local') {
540 $this->localbackend = $this->numbackends;
541 $this->localbackendname = $newback->sname;
542 }
543
544 return $this->numbackends;
545 }
4935919f 546
4935919f 547
4272758c 548 /**
549 * create string with name and email address
550 *
62f7daa5 551 * This function takes a $row array as returned by the addressbook
2e542990 552 * search and returns an e-mail address with the full name or
553 * nickname optionally prepended.
4272758c 554 * @param array $row address book entry
555 * @return string email address with real name prepended
2e542990 556 */
2e542990 557 function full_address($row) {
1ba8cd6b 558 global $addrsrch_fullname, $data_dir, $username;
20ad4fdd 559 $prefix = getPref($data_dir, $username, 'addrsrch_fullname');
560 if (($prefix != "" || (isset($addrsrch_fullname) &&
561 $prefix == $addrsrch_fullname)) && $prefix != 'noprefix') {
562 $name = ($prefix == 'nickname' ? $row['nickname'] : $row['name']);
2e542990 563 return $name . ' <' . trim($row['email']) . '>';
564 } else {
565 return trim($row['email']);
566 }
567 }
568
4272758c 569 /**
570 * Search for entries in address books
571 *
572 * Return a list of addresses matching expression in
573 * all backends of a given type.
574 * @param string $expression search expression
575 * @param integer $bnum backend number. default to search in all backends
576 * @return array search results
577 */
81fa4801 578 function search($expression, $bnum = -1) {
579 $ret = array();
580 $this->error = '';
581
582 /* Search all backends */
583 if ($bnum == -1) {
584 $sel = $this->get_backend_list('');
585 $failed = 0;
586 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
587 $backend = &$sel[$i];
588 $backend->error = '';
589 $res = $backend->search($expression);
590 if (is_array($res)) {
591 $ret = array_merge($ret, $res);
592 } else {
0493ed11 593 $this->error .= "<br />\n" . htmlspecialchars($backend->error);
81fa4801 594 $failed++;
75e19c7f 595 }
596 }
4935919f 597
81fa4801 598 /* Only fail if all backends failed */
599 if( $failed >= sizeof( $sel ) ) {
600 $ret = FALSE;
4935919f 601 }
4935919f 602
81fa4801 603 } else {
4935919f 604
81fa4801 605 /* Search only one backend */
4935919f 606
81fa4801 607 $ret = $this->backends[$bnum]->search($expression);
608 if (!is_array($ret)) {
0493ed11 609 $this->error .= "<br />\n" . htmlspecialchars($this->backends[$bnum]->error);
81fa4801 610 $ret = FALSE;
611 }
612 }
613
614 return( $ret );
4935919f 615 }
616
617
4272758c 618 /**
619 * Sorted search
620 * @param string $expression search expression
621 * @param integer $bnum backend number. default to search in all backends
622 * @return array search results
623 */
81fa4801 624 function s_search($expression, $bnum = -1) {
62f7daa5 625
81fa4801 626 $ret = $this->search($expression, $bnum);
627 if ( is_array( $ret ) ) {
628 usort($ret, 'addressbook_cmp');
62f7daa5 629 }
81fa4801 630 return $ret;
631 }
4935919f 632
633
4272758c 634 /**
635 * Lookup an address by alias.
636 * Only possible in local backends.
637 * @param string $alias
638 * @param integer backend number
639 * @return array lookup results. False, if not found.
81fa4801 640 */
641 function lookup($alias, $bnum = -1) {
62f7daa5 642
81fa4801 643 $ret = array();
62f7daa5 644
81fa4801 645 if ($bnum > -1) {
646 $res = $this->backends[$bnum]->lookup($alias);
647 if (is_array($res)) {
648 return $res;
649 } else {
b6827bc0 650 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 651 return false;
652 }
653 }
62f7daa5 654
81fa4801 655 $sel = $this->get_backend_list('local');
656 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
657 $backend = &$sel[$i];
658 $backend->error = '';
659 $res = $backend->lookup($alias);
660 if (is_array($res)) {
661 if(!empty($res))
662 return $res;
663 } else {
0493ed11 664 $this->error = htmlspecialchars($backend->error);
81fa4801 665 return false;
666 }
667 }
62f7daa5 668
81fa4801 669 return $ret;
4935919f 670 }
671
4935919f 672
4272758c 673 /**
674 * Return all addresses
675 * @param integer $bnum backend number
676 * @return array search results
677 */
81fa4801 678 function list_addr($bnum = -1) {
679 $ret = array();
62f7daa5 680
81fa4801 681 if ($bnum == -1) {
4272758c 682 $sel = $this->get_backend_list('');
81fa4801 683 } else {
684 $sel = array(0 => &$this->backends[$bnum]);
685 }
62f7daa5 686
81fa4801 687 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
688 $backend = &$sel[$i];
689 $backend->error = '';
690 $res = $backend->list_addr();
691 if (is_array($res)) {
692 $ret = array_merge($ret, $res);
693 } else {
0493ed11 694 $this->error = htmlspecialchars($backend->error);
81fa4801 695 return false;
696 }
697 }
62f7daa5 698
81fa4801 699 return $ret;
700 }
4935919f 701
4272758c 702 /**
91e0dccc 703 * Create a new address
4272758c 704 * @param array $userdata added address record
705 * @param integer $bnum backend number
706 * @return integer the backend number that the/ address was added
81fa4801 707 * to, or false if it failed.
708 */
709 function add($userdata, $bnum) {
62f7daa5 710
81fa4801 711 /* Validate data */
712 if (!is_array($userdata)) {
713 $this->error = _("Invalid input data");
714 return false;
715 }
716 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
717 $this->error = _("Name is missing");
718 return false;
719 }
720 if (empty($userdata['email'])) {
721 $this->error = _("E-mail address is missing");
722 return false;
723 }
724 if (empty($userdata['nickname'])) {
725 $userdata['nickname'] = $userdata['email'];
726 }
62f7daa5 727
81fa4801 728 if (eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
729 $this->error = _("Nickname contains illegal characters");
730 return false;
731 }
62f7daa5 732
81fa4801 733 /* Check that specified backend accept new entries */
734 if (!$this->backends[$bnum]->writeable) {
735 $this->error = _("Addressbook is read-only");
736 return false;
737 }
62f7daa5 738
81fa4801 739 /* Add address to backend */
740 $res = $this->backends[$bnum]->add($userdata);
741 if ($res) {
742 return $bnum;
743 } else {
0493ed11 744 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 745 return false;
746 }
62f7daa5 747
81fa4801 748 return false; // Not reached
749 } /* end of add() */
750
751
4272758c 752 /**
753 * Remove the entries from address book
91e0dccc 754 * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
4272758c 755 * @param integer $bnum backend number
756 * @return bool true if removed successfully. false if there s an error. $this->error contains error message
81fa4801 757 */
758 function remove($alias, $bnum) {
62f7daa5 759
81fa4801 760 /* Check input */
761 if (empty($alias)) {
762 return true;
763 }
62f7daa5 764
81fa4801 765 /* Convert string to single element array */
766 if (!is_array($alias)) {
767 $alias = array(0 => $alias);
768 }
62f7daa5 769
770 /* Check that specified backend is writable */
81fa4801 771 if (!$this->backends[$bnum]->writeable) {
772 $this->error = _("Addressbook is read-only");
773 return false;
774 }
62f7daa5 775
81fa4801 776 /* Remove user from backend */
777 $res = $this->backends[$bnum]->remove($alias);
778 if ($res) {
779 return $bnum;
780 } else {
0493ed11 781 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 782 return false;
783 }
62f7daa5 784
81fa4801 785 return FALSE; /* Not reached */
786 } /* end of remove() */
787
788
4272758c 789 /**
790 * Modify entry in address book
791 * @param string $alias nickname
792 * @param array $userdata newdata
793 * @param integer $bnum backend number
81fa4801 794 */
795 function modify($alias, $userdata, $bnum) {
62f7daa5 796
81fa4801 797 /* Check input */
798 if (empty($alias) || !is_string($alias)) {
799 return true;
800 }
62f7daa5 801
81fa4801 802 /* Validate data */
803 if(!is_array($userdata)) {
804 $this->error = _("Invalid input data");
805 return false;
806 }
807 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
808 $this->error = _("Name is missing");
809 return false;
810 }
811 if (empty($userdata['email'])) {
812 $this->error = _("E-mail address is missing");
813 return false;
814 }
62f7daa5 815
81fa4801 816 if (eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
817 $this->error = _("Nickname contains illegal characters");
818 return false;
819 }
62f7daa5 820
81fa4801 821 if (empty($userdata['nickname'])) {
822 $userdata['nickname'] = $userdata['email'];
823 }
62f7daa5 824
825 /* Check that specified backend is writable */
81fa4801 826 if (!$this->backends[$bnum]->writeable) {
827 $this->error = _("Addressbook is read-only");;
828 return false;
829 }
62f7daa5 830
81fa4801 831 /* Modify user in backend */
832 $res = $this->backends[$bnum]->modify($alias, $userdata);
833 if ($res) {
834 return $bnum;
835 } else {
0493ed11 836 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 837 return false;
838 }
62f7daa5 839
81fa4801 840 return FALSE; /* Not reached */
841 } /* end of modify() */
62f7daa5 842
843
81fa4801 844} /* End of class Addressbook */
845
8f6f9ba5 846/**
81fa4801 847 * Generic backend that all other backends extend
8f6f9ba5 848 * @package squirrelmail
c1ac62d4 849 * @subpackage addressbook
81fa4801 850 */
851class addressbook_backend {
852
853 /* Variables that all backends must provide. */
4272758c 854 /**
855 * Backend type
856 *
857 * Can be 'local' or 'remote'
858 * @var string backend type
859 */
81fa4801 860 var $btype = 'dummy';
4272758c 861 /**
862 * Internal backend name
863 * @var string
864 */
81fa4801 865 var $bname = 'dummy';
4272758c 866 /**
867 * Displayed backend name
868 * @var string
869 */
81fa4801 870 var $sname = 'Dummy backend';
62f7daa5 871
81fa4801 872 /*
873 * Variables common for all backends, but that
874 * should not be changed by the backends.
875 */
4272758c 876 /**
877 * Backend number
878 * @var integer
879 */
81fa4801 880 var $bnum = -1;
4272758c 881 /**
882 * Error messages
883 * @var string
884 */
81fa4801 885 var $error = '';
4272758c 886 /**
887 * Writeable flag
888 * @var bool
889 */
81fa4801 890 var $writeable = false;
62f7daa5 891
4272758c 892 /**
893 * Set error message
894 * @param string $string error message
895 * @return bool
896 */
81fa4801 897 function set_error($string) {
898 $this->error = '[' . $this->sname . '] ' . $string;
899 return false;
900 }
62f7daa5 901
902
81fa4801 903 /* ========================== Public ======================== */
62f7daa5 904
4272758c 905 /**
906 * Search for entries in backend
327e2d96 907 *
202bcbcc 908 * Working backend should support use of wildcards. * symbol
327e2d96 909 * should match one or more symbols. ? symbol should match any
202bcbcc 910 * single symbol.
4272758c 911 * @param string $expression
912 * @return bool
913 */
81fa4801 914 function search($expression) {
915 $this->set_error('search not implemented');
916 return false;
917 }
62f7daa5 918
4272758c 919 /**
920 * Find entry in backend by alias
921 * @param string $alias name used for id
922 * @return bool
923 */
81fa4801 924 function lookup($alias) {
925 $this->set_error('lookup not implemented');
926 return false;
a10110a5 927 }
62f7daa5 928
4272758c 929 /**
930 * List all entries in backend
327e2d96 931 *
932 * Working backend should provide this function or at least
933 * dummy function that returns empty array.
4272758c 934 * @return bool
935 */
81fa4801 936 function list_addr() {
937 $this->set_error('list_addr not implemented');
938 return false;
939 }
62f7daa5 940
4272758c 941 /**
942 * Add entry to backend
943 * @param array userdata
944 * @return bool
945 */
81fa4801 946 function add($userdata) {
947 $this->set_error('add not implemented');
948 return false;
949 }
62f7daa5 950
4272758c 951 /**
952 * Remove entry from backend
953 * @param string $alias name used for id
954 * @return bool
955 */
81fa4801 956 function remove($alias) {
957 $this->set_error('delete not implemented');
958 return false;
959 }
62f7daa5 960
4272758c 961 /**
962 * Modify entry in backend
963 * @param string $alias name used for id
964 * @param array $newuserdata new data
965 * @return bool
966 */
81fa4801 967 function modify($alias, $newuserdata) {
968 $this->set_error('modify not implemented');
969 return false;
970 }
81fa4801 971}
867fed37 972?>