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