sq_mt_randomize() is not available in style.php
[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) {
1b858d86 30 global $data_dir, $username, $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
664fd7a0 139
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 continue;
146
147 /* if onlylocal is true, we only add writeable ldap servers */
148 if ($onlylocal && (!isset($param['writeable']) || $param['writeable'] != true))
149 continue;
150
151 $r = $abook->add_backend('ldap_server', $param);
152 if (!$r && $showerr) {
153 if ($abook_init_error!='') $abook_init_error.="<br />\n";
154 $abook_init_error.=sprintf(_("Error initializing LDAP server %s:") .
155 "<br />\n", $param['host']);
156 $abook_init_error.= $abook->error;
81fa4801 157 }
664fd7a0 158 }
159 } // end of ldap server init
4935919f 160
04875ae0 161 /**
162 * display address book init errors.
163 */
164 if ($abook_init_error!='' && $showerr) {
1b858d86 165 error_box($abook_init_error);
04875ae0 166 }
7390e240 167
81fa4801 168 /* Return the initialized object */
169 return $abook;
4935919f 170}
171
c1ac62d4 172/**
173 * Display the "new address" form
174 *
175 * Form is not closed and you must add closing form tag.
176 * @since 1.5.1
177 * @param string $form_url form action url
178 * @param string $name form name
179 * @param string $title form title
180 * @param string $button form button name
181 * @param array $defdata values of form fields
182 */
183function abook_create_form($form_url,$name,$title,$button,$defdata=array()) {
184 global $color;
185 echo addForm($form_url, 'post', 'f_add').
186 html_tag( 'table',
187 html_tag( 'tr',
188 html_tag( 'td', "\n". '<strong>' . $title . '</strong>' . "\n",
189 'center', $color[0]
190 )
191 )
81642286 192 , 'center', '', 'width="90%"' ) ."\n";
c1ac62d4 193 address_form($name, $button, $defdata);
194}
195
4935919f 196
327e2d96 197/**
81fa4801 198 * Had to move this function outside of the Addressbook Class
199 * PHP 4.0.4 Seemed to be having problems with inline functions.
abd74f7d 200 * Note: this can return now since we don't support 4.0.4 anymore.
62f7daa5 201 */
81fa4801 202function addressbook_cmp($a,$b) {
4935919f 203
81fa4801 204 if($a['backend'] > $b['backend']) {
205 return 1;
206 } else if($a['backend'] < $b['backend']) {
207 return -1;
208 }
62f7daa5 209
81fa4801 210 return (strtolower($a['name']) > strtolower($b['name'])) ? 1 : -1;
4935919f 211
81fa4801 212}
4935919f 213
c1ac62d4 214/**
215 * Make an input field
216 * @param string $label
217 * @param string $field
218 * @param string $name
219 * @param string $size
220 * @param array $values
221 * @param string $add
222 */
223function addressbook_inp_field($label, $field, $name, $size, $values, $add='') {
224 global $color;
225 $value = ( isset($values[$field]) ? $values[$field] : '');
226
227 if (is_array($value)) {
228 $td_str = addSelect($name.'['.$field.']', $value);
229 } else {
230 $td_str = addInput($name.'['.$field.']', $value, $size);
231 }
232 $td_str .= $add ;
233
234 return html_tag( 'tr' ,
235 html_tag( 'td', $label . ':', 'right', $color[4]) .
236 html_tag( 'td', $td_str, 'left', $color[4])
237 )
238 . "\n";
239}
240
241/**
242 * Output form to add and modify address data
243 */
244function address_form($name, $submittext, $values = array()) {
245 global $color, $squirrelmail_language;
246
247 if ($squirrelmail_language == 'ja_JP') {
248 echo html_tag( 'table',
249 addressbook_inp_field(_("Nickname"), 'nickname', $name, 15, $values,
250 ' <small>' . _("Must be unique") . '</small>') .
251 addressbook_inp_field(_("E-mail address"), 'email', $name, 45, $values, '') .
252 addressbook_inp_field(_("Last name"), 'lastname', $name, 45, $values, '') .
253 addressbook_inp_field(_("First name"), 'firstname', $name, 45, $values, '') .
254 addressbook_inp_field(_("Additional info"), 'label', $name, 45, $values, '') .
255 list_writable_backends($name) .
256 html_tag( 'tr',
257 html_tag( 'td',
258 addSubmit($submittext, $name.'[SUBMIT]'),
259 'center', $color[4], 'colspan="2"')
260 )
261 , 'center', '', 'border="0" cellpadding="1" width="90%"') ."\n";
262 } else {
263 echo html_tag( 'table',
264 addressbook_inp_field(_("Nickname"), 'nickname', $name, 15, $values,
265 ' <small>' . _("Must be unique") . '</small>') .
266 addressbook_inp_field(_("E-mail address"), 'email', $name, 45, $values, '') .
267 addressbook_inp_field(_("First name"), 'firstname', $name, 45, $values, '') .
268 addressbook_inp_field(_("Last name"), 'lastname', $name, 45, $values, '') .
269 addressbook_inp_field(_("Additional info"), 'label', $name, 45, $values, '') .
270 list_writable_backends($name) .
271 html_tag( 'tr',
272 html_tag( 'td',
273 addSubmit($submittext, $name.'[SUBMIT]') ,
274 'center', $color[4], 'colspan="2"')
275 )
276 , 'center', '', 'border="0" cellpadding="1" width="90%"') ."\n";
277 }
278}
279
6ad2bbe2 280/**
281 * Provides list of writeable backends.
282 * Works only when address is added ($name='addaddr')
283 * @param string $name name of form
284 * @return string html formated backend field (select or hidden)
285 */
c1ac62d4 286function list_writable_backends($name) {
287 global $color, $abook;
288 if ( $name != 'addaddr' ) { return; }
6ad2bbe2 289 $writeable_abook = 1;
c1ac62d4 290 if ( $abook->numbackends > 1 ) {
c1ac62d4 291 $backends = $abook->get_backend_list();
6ad2bbe2 292 $writeable_abooks=array();
c1ac62d4 293 while (list($undef,$v) = each($backends)) {
294 if ($v->writeable) {
6ad2bbe2 295 // add each backend to array
296 $writeable_abooks[$v->bnum]=$v->sname;
297 // save backend number
298 $writeable_abook=$v->bnum;
c1ac62d4 299 }
300 }
6ad2bbe2 301 if (count($writeable_abooks)>1) {
302 // we have more than one writeable backend
303 $ret=addSelect('backend',$writeable_abooks,null,true);
304 return html_tag( 'tr',
305 html_tag( 'td', _("Add to:"),'right', $color[4] ) .
306 html_tag( 'td', $ret, 'left', $color[4] )) . "\n";
307 }
c1ac62d4 308 }
6ad2bbe2 309 // Only one backend exists or is writeable.
310 return html_tag( 'tr',
311 html_tag( 'td',
312 addHidden('backend', $writeable_abook),
313 'center', $color[4], 'colspan="2"')) . "\n";
c1ac62d4 314}
315
316/**
317 * Sort array by the key "name"
318 */
319function alistcmp($a,$b) {
320 $abook_sort_order=get_abook_sort();
321
322 switch ($abook_sort_order) {
323 case 0:
324 case 1:
325 $abook_sort='nickname';
326 break;
327 case 4:
328 case 5:
329 $abook_sort='email';
330 break;
331 case 6:
332 case 7:
333 $abook_sort='label';
334 break;
335 case 2:
336 case 3:
337 case 8:
338 default:
339 $abook_sort='name';
340 }
341
342 if ($a['backend'] > $b['backend']) {
343 return 1;
344 } else {
345 if ($a['backend'] < $b['backend']) {
346 return -1;
347 }
348 }
349
350 if( (($abook_sort_order+2) % 2) == 1) {
351 return (strtolower($a[$abook_sort]) < strtolower($b[$abook_sort])) ? 1 : -1;
352 } else {
353 return (strtolower($a[$abook_sort]) > strtolower($b[$abook_sort])) ? 1 : -1;
354 }
355}
356
357/**
358 * Address book sorting options
359 *
360 * returns address book sorting order
361 * @return integer book sorting options order
362 */
363function get_abook_sort() {
364 global $data_dir, $username;
365
366 /* get sorting order */
367 if(sqgetGlobalVar('abook_sort_order', $temp, SQ_GET)) {
368 $abook_sort_order = (int) $temp;
369
370 if ($abook_sort_order < 0 or $abook_sort_order > 8)
371 $abook_sort_order=8;
372
373 setPref($data_dir, $username, 'abook_sort_order', $abook_sort_order);
374 } else {
375 /* get previous sorting options. default to unsorted */
376 $abook_sort_order = getPref($data_dir, $username, 'abook_sort_order', 8);
377 }
378
379 return $abook_sort_order;
380}
381
382/**
383 * This function shows the address book sort button.
384 *
385 * @param integer $abook_sort_order current sort value
386 * @param string $alt_tag alt tag value (string visible to text only browsers)
387 * @param integer $Down sort value when list is sorted ascending
388 * @param integer $Up sort value when list is sorted descending
389 * @return string html code with sorting images and urls
390 */
391function show_abook_sort_button($abook_sort_order, $alt_tag, $Down, $Up ) {
635f7200 392 global $form_url, $icon_theme_path;
c1ac62d4 393
394 /* Figure out which image we want to use. */
395 if ($abook_sort_order != $Up && $abook_sort_order != $Down) {
396 $img = 'sort_none.png';
de783fdf 397 $text_icon = '&#9723;'; // U+25FB WHITE MEDIUM SQUARE
c1ac62d4 398 $which = $Up;
399 } elseif ($abook_sort_order == $Up) {
400 $img = 'up_pointer.png';
de783fdf 401 $text_icon = '&#8679;'; // U+21E7 UPWARDS WHITE ARROW
c1ac62d4 402 $which = $Down;
403 } else {
404 $img = 'down_pointer.png';
de783fdf 405 $text_icon = '&#8681;'; // U+21E9 DOWNWARDS WHITE ARROW
c1ac62d4 406 $which = 8;
407 }
408
635f7200 409 /* Now that we have everything figured out, show the actual button. */
de783fdf 410 return '&nbsp;<a href="' . $form_url .'?abook_sort_order=' . $which .
411 '" style="text-decoration:none" title="'.$alt_tag.'">' .
412 getIcon($icon_theme_path, $img, $text_icon, $alt_tag) .
635f7200 413 '</a>';
c1ac62d4 414}
415
4935919f 416
8f6f9ba5 417/**
81fa4801 418 * This is the main address book class that connect all the
419 * backends and provide services to the functions above.
8f6f9ba5 420 * @package squirrelmail
c1ac62d4 421 * @subpackage addressbook
81fa4801 422 */
81fa4801 423class AddressBook {
0493ed11 424
425 /*
426 Cleaning errors from html with htmlspecialchars:
427 Errors from the backend are cleaned up in this class because we not always
428 have control over it when error output is generated in the backend.
429 If this appears to be wrong place then clean it up at the source (the backend)
0493ed11 430 */
431
4272758c 432 /**
433 * Enabled address book backends
434 * @var array
435 */
81fa4801 436 var $backends = array();
4272758c 437 /**
438 * Number of enabled backends
439 * @var integer
440 */
81fa4801 441 var $numbackends = 0;
4272758c 442 /**
443 * Error messages
444 * @var string
445 */
81fa4801 446 var $error = '';
4272758c 447 /**
448 * id of backend with personal address book
449 * @var integer
450 */
81fa4801 451 var $localbackend = 0;
4272758c 452 /**
453 * Name of backend with personal address book
454 * @var string
455 */
81fa4801 456 var $localbackendname = '';
7b0ea860 457 /**
458 * Controls use of 'extra' field
202bcbcc 459 *
460 * Extra field can be used to add link to form, which allows
461 * to modify all fields supported by backend. This is the only field
7b0ea860 462 * that is not sanitized with htmlspecialchars. Backends MUST make
463 * sure that field data is sanitized and displayed correctly inside
464 * table cell. Use of html formating in other address book fields is
202bcbcc 465 * not allowed. Backends that don't return 'extra' row in address book
7b0ea860 466 * data should not modify this object property.
467 * @var boolean
468 * @since 1.5.1
469 */
470 var $add_extra_field = false;
62f7daa5 471
4272758c 472 /**
473 * Constructor function.
474 */
81fa4801 475 function AddressBook() {
c6b8b46c 476 $this->localbackendname = _("Personal address book");
81fa4801 477 }
4935919f 478
4272758c 479 /**
81fa4801 480 * Return an array of backends of a given type,
481 * or all backends if no type is specified.
4272758c 482 * @param string $type backend type
483 * @return array list of backends
81fa4801 484 */
485 function get_backend_list($type = '') {
486 $ret = array();
487 for ($i = 1 ; $i <= $this->numbackends ; $i++) {
488 if (empty($type) || $type == $this->backends[$i]->btype) {
489 $ret[] = &$this->backends[$i];
490 }
4935919f 491 }
81fa4801 492 return $ret;
493 }
4935919f 494
495
4272758c 496 /* ========================== Public ======================== */
81fa4801 497
4272758c 498 /**
499 * Add a new backend.
500 *
501 * @param string $backend backend name (without the abook_ prefix)
502 * @param mixed optional variable that is passed to the backend constructor.
503 * See each of the backend classes for valid parameters
504 * @return integer number of backends
81fa4801 505 */
506 function add_backend($backend, $param = '') {
202bcbcc 507 static $backend_classes;
508 if (!isset($backend_classes)) {
509 $backend_classes = array();
510 }
511 if (!isset($backend_classes[$backend])) {
512 /**
513 * Support backend provided by plugins. Plugin function must
514 * return an associative array with as key the backend name ($backend)
515 * and as value the file including the path containing the backend class.
516 * i.e.: $aBackend = array('backend_template' => SM_PATH . 'plugins/abook_backend_template/functions.php')
517 *
518 * NB: Because the backend files are included from within this function they DO NOT have access to
519 * vars in the global scope. This function is the global scope for the included backend !!!
520 */
521 $aBackend = do_hook('abook_add_class');
522 if (isset($aBackend) && is_array($aBackend) && isset($aBackend[$backend])) {
523 require_once($aBackend[$backend]);
524 } else {
525 require_once(SM_PATH . 'functions/abook_'.$backend.'.php');
526 }
527 $backend_classes[$backend] = true;
528 }
81fa4801 529 $backend_name = 'abook_' . $backend;
202bcbcc 530 $newback = new $backend_name($param);
531 //eval('$newback = new ' . $backend_name . '($param);');
81fa4801 532 if(!empty($newback->error)) {
533 $this->error = $newback->error;
534 return false;
535 }
536
537 $this->numbackends++;
538
539 $newback->bnum = $this->numbackends;
540 $this->backends[$this->numbackends] = $newback;
62f7daa5 541
81fa4801 542 /* Store ID of first local backend added */
543 if ($this->localbackend == 0 && $newback->btype == 'local') {
544 $this->localbackend = $this->numbackends;
545 $this->localbackendname = $newback->sname;
546 }
547
548 return $this->numbackends;
549 }
4935919f 550
4935919f 551
4272758c 552 /**
553 * create string with name and email address
554 *
62f7daa5 555 * This function takes a $row array as returned by the addressbook
2e542990 556 * search and returns an e-mail address with the full name or
557 * nickname optionally prepended.
4272758c 558 * @param array $row address book entry
559 * @return string email address with real name prepended
2e542990 560 */
2e542990 561 function full_address($row) {
1ba8cd6b 562 global $addrsrch_fullname, $data_dir, $username;
20ad4fdd 563 $prefix = getPref($data_dir, $username, 'addrsrch_fullname');
564 if (($prefix != "" || (isset($addrsrch_fullname) &&
565 $prefix == $addrsrch_fullname)) && $prefix != 'noprefix') {
566 $name = ($prefix == 'nickname' ? $row['nickname'] : $row['name']);
2e542990 567 return $name . ' <' . trim($row['email']) . '>';
568 } else {
569 return trim($row['email']);
570 }
571 }
572
4272758c 573 /**
574 * Search for entries in address books
575 *
576 * Return a list of addresses matching expression in
577 * all backends of a given type.
578 * @param string $expression search expression
579 * @param integer $bnum backend number. default to search in all backends
580 * @return array search results
581 */
81fa4801 582 function search($expression, $bnum = -1) {
583 $ret = array();
584 $this->error = '';
585
586 /* Search all backends */
587 if ($bnum == -1) {
588 $sel = $this->get_backend_list('');
589 $failed = 0;
590 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
591 $backend = &$sel[$i];
592 $backend->error = '';
593 $res = $backend->search($expression);
594 if (is_array($res)) {
595 $ret = array_merge($ret, $res);
596 } else {
0493ed11 597 $this->error .= "<br />\n" . htmlspecialchars($backend->error);
81fa4801 598 $failed++;
75e19c7f 599 }
600 }
4935919f 601
81fa4801 602 /* Only fail if all backends failed */
603 if( $failed >= sizeof( $sel ) ) {
604 $ret = FALSE;
4935919f 605 }
4935919f 606
81fa4801 607 } else {
4935919f 608
81fa4801 609 /* Search only one backend */
4935919f 610
81fa4801 611 $ret = $this->backends[$bnum]->search($expression);
612 if (!is_array($ret)) {
0493ed11 613 $this->error .= "<br />\n" . htmlspecialchars($this->backends[$bnum]->error);
81fa4801 614 $ret = FALSE;
615 }
616 }
617
618 return( $ret );
4935919f 619 }
620
621
4272758c 622 /**
623 * Sorted search
624 * @param string $expression search expression
625 * @param integer $bnum backend number. default to search in all backends
626 * @return array search results
627 */
81fa4801 628 function s_search($expression, $bnum = -1) {
62f7daa5 629
81fa4801 630 $ret = $this->search($expression, $bnum);
631 if ( is_array( $ret ) ) {
632 usort($ret, 'addressbook_cmp');
62f7daa5 633 }
81fa4801 634 return $ret;
635 }
4935919f 636
637
4272758c 638 /**
639 * Lookup an address by alias.
640 * Only possible in local backends.
641 * @param string $alias
642 * @param integer backend number
643 * @return array lookup results. False, if not found.
81fa4801 644 */
645 function lookup($alias, $bnum = -1) {
62f7daa5 646
81fa4801 647 $ret = array();
62f7daa5 648
81fa4801 649 if ($bnum > -1) {
650 $res = $this->backends[$bnum]->lookup($alias);
651 if (is_array($res)) {
652 return $res;
653 } else {
b6827bc0 654 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 655 return false;
656 }
657 }
62f7daa5 658
81fa4801 659 $sel = $this->get_backend_list('local');
660 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
661 $backend = &$sel[$i];
662 $backend->error = '';
663 $res = $backend->lookup($alias);
664 if (is_array($res)) {
665 if(!empty($res))
666 return $res;
667 } else {
0493ed11 668 $this->error = htmlspecialchars($backend->error);
81fa4801 669 return false;
670 }
671 }
62f7daa5 672
81fa4801 673 return $ret;
4935919f 674 }
675
4935919f 676
4272758c 677 /**
678 * Return all addresses
679 * @param integer $bnum backend number
680 * @return array search results
681 */
81fa4801 682 function list_addr($bnum = -1) {
683 $ret = array();
62f7daa5 684
81fa4801 685 if ($bnum == -1) {
4272758c 686 $sel = $this->get_backend_list('');
81fa4801 687 } else {
688 $sel = array(0 => &$this->backends[$bnum]);
689 }
62f7daa5 690
81fa4801 691 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
692 $backend = &$sel[$i];
693 $backend->error = '';
694 $res = $backend->list_addr();
695 if (is_array($res)) {
696 $ret = array_merge($ret, $res);
697 } else {
0493ed11 698 $this->error = htmlspecialchars($backend->error);
81fa4801 699 return false;
700 }
701 }
62f7daa5 702
81fa4801 703 return $ret;
704 }
4935919f 705
4272758c 706 /**
91e0dccc 707 * Create a new address
4272758c 708 * @param array $userdata added address record
709 * @param integer $bnum backend number
710 * @return integer the backend number that the/ address was added
81fa4801 711 * to, or false if it failed.
712 */
713 function add($userdata, $bnum) {
62f7daa5 714
81fa4801 715 /* Validate data */
716 if (!is_array($userdata)) {
717 $this->error = _("Invalid input data");
718 return false;
719 }
720 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
721 $this->error = _("Name is missing");
722 return false;
723 }
724 if (empty($userdata['email'])) {
725 $this->error = _("E-mail address is missing");
726 return false;
727 }
728 if (empty($userdata['nickname'])) {
729 $userdata['nickname'] = $userdata['email'];
730 }
62f7daa5 731
81fa4801 732 if (eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
733 $this->error = _("Nickname contains illegal characters");
734 return false;
735 }
62f7daa5 736
81fa4801 737 /* Check that specified backend accept new entries */
738 if (!$this->backends[$bnum]->writeable) {
739 $this->error = _("Addressbook is read-only");
740 return false;
741 }
62f7daa5 742
81fa4801 743 /* Add address to backend */
744 $res = $this->backends[$bnum]->add($userdata);
745 if ($res) {
746 return $bnum;
747 } else {
0493ed11 748 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 749 return false;
750 }
62f7daa5 751
81fa4801 752 return false; // Not reached
753 } /* end of add() */
754
755
4272758c 756 /**
757 * Remove the entries from address book
91e0dccc 758 * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
4272758c 759 * @param integer $bnum backend number
760 * @return bool true if removed successfully. false if there s an error. $this->error contains error message
81fa4801 761 */
762 function remove($alias, $bnum) {
62f7daa5 763
81fa4801 764 /* Check input */
765 if (empty($alias)) {
766 return true;
767 }
62f7daa5 768
81fa4801 769 /* Convert string to single element array */
770 if (!is_array($alias)) {
771 $alias = array(0 => $alias);
772 }
62f7daa5 773
774 /* Check that specified backend is writable */
81fa4801 775 if (!$this->backends[$bnum]->writeable) {
776 $this->error = _("Addressbook is read-only");
777 return false;
778 }
62f7daa5 779
81fa4801 780 /* Remove user from backend */
781 $res = $this->backends[$bnum]->remove($alias);
782 if ($res) {
783 return $bnum;
784 } else {
0493ed11 785 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 786 return false;
787 }
62f7daa5 788
81fa4801 789 return FALSE; /* Not reached */
790 } /* end of remove() */
791
792
4272758c 793 /**
794 * Modify entry in address book
795 * @param string $alias nickname
796 * @param array $userdata newdata
797 * @param integer $bnum backend number
81fa4801 798 */
799 function modify($alias, $userdata, $bnum) {
62f7daa5 800
81fa4801 801 /* Check input */
802 if (empty($alias) || !is_string($alias)) {
803 return true;
804 }
62f7daa5 805
81fa4801 806 /* Validate data */
807 if(!is_array($userdata)) {
808 $this->error = _("Invalid input data");
809 return false;
810 }
811 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
812 $this->error = _("Name is missing");
813 return false;
814 }
815 if (empty($userdata['email'])) {
816 $this->error = _("E-mail address is missing");
817 return false;
818 }
62f7daa5 819
81fa4801 820 if (eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
821 $this->error = _("Nickname contains illegal characters");
822 return false;
823 }
62f7daa5 824
81fa4801 825 if (empty($userdata['nickname'])) {
826 $userdata['nickname'] = $userdata['email'];
827 }
62f7daa5 828
829 /* Check that specified backend is writable */
81fa4801 830 if (!$this->backends[$bnum]->writeable) {
831 $this->error = _("Addressbook is read-only");;
832 return false;
833 }
62f7daa5 834
81fa4801 835 /* Modify user in backend */
836 $res = $this->backends[$bnum]->modify($alias, $userdata);
837 if ($res) {
838 return $bnum;
839 } else {
0493ed11 840 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 841 return false;
842 }
62f7daa5 843
81fa4801 844 return FALSE; /* Not reached */
845 } /* end of modify() */
62f7daa5 846
847
81fa4801 848} /* End of class Addressbook */
849
8f6f9ba5 850/**
81fa4801 851 * Generic backend that all other backends extend
8f6f9ba5 852 * @package squirrelmail
c1ac62d4 853 * @subpackage addressbook
81fa4801 854 */
855class addressbook_backend {
856
857 /* Variables that all backends must provide. */
4272758c 858 /**
859 * Backend type
860 *
861 * Can be 'local' or 'remote'
862 * @var string backend type
863 */
81fa4801 864 var $btype = 'dummy';
4272758c 865 /**
866 * Internal backend name
867 * @var string
868 */
81fa4801 869 var $bname = 'dummy';
4272758c 870 /**
871 * Displayed backend name
872 * @var string
873 */
81fa4801 874 var $sname = 'Dummy backend';
62f7daa5 875
81fa4801 876 /*
877 * Variables common for all backends, but that
878 * should not be changed by the backends.
879 */
4272758c 880 /**
881 * Backend number
882 * @var integer
883 */
81fa4801 884 var $bnum = -1;
4272758c 885 /**
886 * Error messages
887 * @var string
888 */
81fa4801 889 var $error = '';
4272758c 890 /**
891 * Writeable flag
892 * @var bool
893 */
81fa4801 894 var $writeable = false;
62f7daa5 895
4272758c 896 /**
897 * Set error message
898 * @param string $string error message
899 * @return bool
900 */
81fa4801 901 function set_error($string) {
902 $this->error = '[' . $this->sname . '] ' . $string;
903 return false;
904 }
62f7daa5 905
906
81fa4801 907 /* ========================== Public ======================== */
62f7daa5 908
4272758c 909 /**
910 * Search for entries in backend
327e2d96 911 *
202bcbcc 912 * Working backend should support use of wildcards. * symbol
327e2d96 913 * should match one or more symbols. ? symbol should match any
202bcbcc 914 * single symbol.
4272758c 915 * @param string $expression
916 * @return bool
917 */
81fa4801 918 function search($expression) {
919 $this->set_error('search not implemented');
920 return false;
921 }
62f7daa5 922
4272758c 923 /**
924 * Find entry in backend by alias
925 * @param string $alias name used for id
926 * @return bool
927 */
81fa4801 928 function lookup($alias) {
929 $this->set_error('lookup not implemented');
930 return false;
a10110a5 931 }
62f7daa5 932
4272758c 933 /**
934 * List all entries in backend
327e2d96 935 *
936 * Working backend should provide this function or at least
937 * dummy function that returns empty array.
4272758c 938 * @return bool
939 */
81fa4801 940 function list_addr() {
941 $this->set_error('list_addr not implemented');
942 return false;
943 }
62f7daa5 944
4272758c 945 /**
946 * Add entry to backend
947 * @param array userdata
948 * @return bool
949 */
81fa4801 950 function add($userdata) {
951 $this->set_error('add not implemented');
952 return false;
953 }
62f7daa5 954
4272758c 955 /**
956 * Remove entry from backend
957 * @param string $alias name used for id
958 * @return bool
959 */
81fa4801 960 function remove($alias) {
961 $this->set_error('delete not implemented');
962 return false;
963 }
62f7daa5 964
4272758c 965 /**
966 * Modify entry in backend
967 * @param string $alias name used for id
968 * @param array $newuserdata new data
969 * @return bool
970 */
81fa4801 971 function modify($alias, $newuserdata) {
972 $this->set_error('modify not implemented');
973 return false;
974 }
81fa4801 975}