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