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