XSS fixes
[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 *
6c84ba1e 6 * Copyright (c) 1999-2005 The SquirrelMail Project Team
35586184 7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
6ad2bbe2 9 * Functions require SM_PATH and support of forms.php functions
35586184 10 *
a9d318b0 11 * @version $Id$
d6c32258 12 * @package squirrelmail
a9d318b0 13 * @subpackage addressbook
35586184 14 */
15
04875ae0 16/**
17 * If SM_PATH isn't defined, define it. Required to include files.
18 * @ignore
19 */
20if (!defined('SM_PATH')) {
21 define('SM_PATH','../');
22}
23
24/* make sure that display_messages.php is loaded */
25include_once(SM_PATH . 'functions/display_messages.php');
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
81fa4801 194/*
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 ) {
389 global $form_url;
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
403 /* Now that we have everything figured out, show the actual button. */
404 return ' <a href="' . $form_url .'?abook_sort_order=' . $which
405 . '"><img src="../images/' . $img
406 . '" border="0" width="12" height="10" alt="' . $alt_tag . '" title="'
407 . _("Click here to change the sorting of the address list") .'" /></a>';
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)
424 Possible problems, translated strings in utf8? (stekkel)
425 */
426
4272758c 427 /**
428 * Enabled address book backends
429 * @var array
430 */
81fa4801 431 var $backends = array();
4272758c 432 /**
433 * Number of enabled backends
434 * @var integer
435 */
81fa4801 436 var $numbackends = 0;
4272758c 437 /**
438 * Error messages
439 * @var string
440 */
81fa4801 441 var $error = '';
4272758c 442 /**
443 * id of backend with personal address book
444 * @var integer
445 */
81fa4801 446 var $localbackend = 0;
4272758c 447 /**
448 * Name of backend with personal address book
449 * @var string
450 */
81fa4801 451 var $localbackendname = '';
62f7daa5 452
4272758c 453 /**
454 * Constructor function.
455 */
81fa4801 456 function AddressBook() {
c6b8b46c 457 $this->localbackendname = _("Personal address book");
81fa4801 458 }
4935919f 459
4272758c 460 /**
81fa4801 461 * Return an array of backends of a given type,
462 * or all backends if no type is specified.
4272758c 463 * @param string $type backend type
464 * @return array list of backends
81fa4801 465 */
466 function get_backend_list($type = '') {
467 $ret = array();
468 for ($i = 1 ; $i <= $this->numbackends ; $i++) {
469 if (empty($type) || $type == $this->backends[$i]->btype) {
470 $ret[] = &$this->backends[$i];
471 }
4935919f 472 }
81fa4801 473 return $ret;
474 }
4935919f 475
476
4272758c 477 /* ========================== Public ======================== */
81fa4801 478
4272758c 479 /**
480 * Add a new backend.
481 *
482 * @param string $backend backend name (without the abook_ prefix)
483 * @param mixed optional variable that is passed to the backend constructor.
484 * See each of the backend classes for valid parameters
485 * @return integer number of backends
81fa4801 486 */
487 function add_backend($backend, $param = '') {
488 $backend_name = 'abook_' . $backend;
489 eval('$newback = new ' . $backend_name . '($param);');
490 if(!empty($newback->error)) {
491 $this->error = $newback->error;
492 return false;
493 }
494
495 $this->numbackends++;
496
497 $newback->bnum = $this->numbackends;
498 $this->backends[$this->numbackends] = $newback;
62f7daa5 499
81fa4801 500 /* Store ID of first local backend added */
501 if ($this->localbackend == 0 && $newback->btype == 'local') {
502 $this->localbackend = $this->numbackends;
503 $this->localbackendname = $newback->sname;
504 }
505
506 return $this->numbackends;
507 }
4935919f 508
4935919f 509
4272758c 510 /**
511 * create string with name and email address
512 *
62f7daa5 513 * This function takes a $row array as returned by the addressbook
2e542990 514 * search and returns an e-mail address with the full name or
515 * nickname optionally prepended.
4272758c 516 * @param array $row address book entry
517 * @return string email address with real name prepended
2e542990 518 */
2e542990 519 function full_address($row) {
1ba8cd6b 520 global $addrsrch_fullname, $data_dir, $username;
20ad4fdd 521 $prefix = getPref($data_dir, $username, 'addrsrch_fullname');
522 if (($prefix != "" || (isset($addrsrch_fullname) &&
523 $prefix == $addrsrch_fullname)) && $prefix != 'noprefix') {
524 $name = ($prefix == 'nickname' ? $row['nickname'] : $row['name']);
2e542990 525 return $name . ' <' . trim($row['email']) . '>';
526 } else {
527 return trim($row['email']);
528 }
529 }
530
4272758c 531 /**
532 * Search for entries in address books
533 *
534 * Return a list of addresses matching expression in
535 * all backends of a given type.
536 * @param string $expression search expression
537 * @param integer $bnum backend number. default to search in all backends
538 * @return array search results
539 */
81fa4801 540 function search($expression, $bnum = -1) {
541 $ret = array();
542 $this->error = '';
543
544 /* Search all backends */
545 if ($bnum == -1) {
546 $sel = $this->get_backend_list('');
547 $failed = 0;
548 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
549 $backend = &$sel[$i];
550 $backend->error = '';
551 $res = $backend->search($expression);
552 if (is_array($res)) {
553 $ret = array_merge($ret, $res);
554 } else {
0493ed11 555 $this->error .= "<br />\n" . htmlspecialchars($backend->error);
81fa4801 556 $failed++;
75e19c7f 557 }
558 }
4935919f 559
81fa4801 560 /* Only fail if all backends failed */
561 if( $failed >= sizeof( $sel ) ) {
562 $ret = FALSE;
4935919f 563 }
4935919f 564
81fa4801 565 } else {
4935919f 566
81fa4801 567 /* Search only one backend */
4935919f 568
81fa4801 569 $ret = $this->backends[$bnum]->search($expression);
570 if (!is_array($ret)) {
0493ed11 571 $this->error .= "<br />\n" . htmlspecialchars($this->backends[$bnum]->error);
81fa4801 572 $ret = FALSE;
573 }
574 }
575
576 return( $ret );
4935919f 577 }
578
579
4272758c 580 /**
581 * Sorted search
582 * @param string $expression search expression
583 * @param integer $bnum backend number. default to search in all backends
584 * @return array search results
585 */
81fa4801 586 function s_search($expression, $bnum = -1) {
62f7daa5 587
81fa4801 588 $ret = $this->search($expression, $bnum);
589 if ( is_array( $ret ) ) {
590 usort($ret, 'addressbook_cmp');
62f7daa5 591 }
81fa4801 592 return $ret;
593 }
4935919f 594
595
4272758c 596 /**
597 * Lookup an address by alias.
598 * Only possible in local backends.
599 * @param string $alias
600 * @param integer backend number
601 * @return array lookup results. False, if not found.
81fa4801 602 */
603 function lookup($alias, $bnum = -1) {
62f7daa5 604
81fa4801 605 $ret = array();
62f7daa5 606
81fa4801 607 if ($bnum > -1) {
608 $res = $this->backends[$bnum]->lookup($alias);
609 if (is_array($res)) {
610 return $res;
611 } else {
0493ed11 612 $this->error = htmlspecialchars($backend->error);
81fa4801 613 return false;
614 }
615 }
62f7daa5 616
81fa4801 617 $sel = $this->get_backend_list('local');
618 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
619 $backend = &$sel[$i];
620 $backend->error = '';
621 $res = $backend->lookup($alias);
622 if (is_array($res)) {
623 if(!empty($res))
624 return $res;
625 } else {
0493ed11 626 $this->error = htmlspecialchars($backend->error);
81fa4801 627 return false;
628 }
629 }
62f7daa5 630
81fa4801 631 return $ret;
4935919f 632 }
633
4935919f 634
4272758c 635 /**
636 * Return all addresses
637 * @param integer $bnum backend number
638 * @return array search results
639 */
81fa4801 640 function list_addr($bnum = -1) {
641 $ret = array();
62f7daa5 642
81fa4801 643 if ($bnum == -1) {
4272758c 644 $sel = $this->get_backend_list('');
81fa4801 645 } else {
646 $sel = array(0 => &$this->backends[$bnum]);
647 }
62f7daa5 648
81fa4801 649 for ($i = 0 ; $i < sizeof($sel) ; $i++) {
650 $backend = &$sel[$i];
651 $backend->error = '';
652 $res = $backend->list_addr();
653 if (is_array($res)) {
654 $ret = array_merge($ret, $res);
655 } else {
0493ed11 656 $this->error = htmlspecialchars($backend->error);
81fa4801 657 return false;
658 }
659 }
62f7daa5 660
81fa4801 661 return $ret;
662 }
4935919f 663
4272758c 664 /**
91e0dccc 665 * Create a new address
4272758c 666 * @param array $userdata added address record
667 * @param integer $bnum backend number
668 * @return integer the backend number that the/ address was added
81fa4801 669 * to, or false if it failed.
670 */
671 function add($userdata, $bnum) {
62f7daa5 672
81fa4801 673 /* Validate data */
674 if (!is_array($userdata)) {
675 $this->error = _("Invalid input data");
676 return false;
677 }
678 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
679 $this->error = _("Name is missing");
680 return false;
681 }
682 if (empty($userdata['email'])) {
683 $this->error = _("E-mail address is missing");
684 return false;
685 }
686 if (empty($userdata['nickname'])) {
687 $userdata['nickname'] = $userdata['email'];
688 }
62f7daa5 689
81fa4801 690 if (eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
691 $this->error = _("Nickname contains illegal characters");
692 return false;
693 }
62f7daa5 694
81fa4801 695 /* Check that specified backend accept new entries */
696 if (!$this->backends[$bnum]->writeable) {
697 $this->error = _("Addressbook is read-only");
698 return false;
699 }
62f7daa5 700
81fa4801 701 /* Add address to backend */
702 $res = $this->backends[$bnum]->add($userdata);
703 if ($res) {
704 return $bnum;
705 } else {
0493ed11 706 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 707 return false;
708 }
62f7daa5 709
81fa4801 710 return false; // Not reached
711 } /* end of add() */
712
713
4272758c 714 /**
715 * Remove the entries from address book
91e0dccc 716 * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
4272758c 717 * @param integer $bnum backend number
718 * @return bool true if removed successfully. false if there s an error. $this->error contains error message
81fa4801 719 */
720 function remove($alias, $bnum) {
62f7daa5 721
81fa4801 722 /* Check input */
723 if (empty($alias)) {
724 return true;
725 }
62f7daa5 726
81fa4801 727 /* Convert string to single element array */
728 if (!is_array($alias)) {
729 $alias = array(0 => $alias);
730 }
62f7daa5 731
732 /* Check that specified backend is writable */
81fa4801 733 if (!$this->backends[$bnum]->writeable) {
734 $this->error = _("Addressbook is read-only");
735 return false;
736 }
62f7daa5 737
81fa4801 738 /* Remove user from backend */
739 $res = $this->backends[$bnum]->remove($alias);
740 if ($res) {
741 return $bnum;
742 } else {
0493ed11 743 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 744 return false;
745 }
62f7daa5 746
81fa4801 747 return FALSE; /* Not reached */
748 } /* end of remove() */
749
750
4272758c 751 /**
752 * Modify entry in address book
753 * @param string $alias nickname
754 * @param array $userdata newdata
755 * @param integer $bnum backend number
81fa4801 756 */
757 function modify($alias, $userdata, $bnum) {
62f7daa5 758
81fa4801 759 /* Check input */
760 if (empty($alias) || !is_string($alias)) {
761 return true;
762 }
62f7daa5 763
81fa4801 764 /* Validate data */
765 if(!is_array($userdata)) {
766 $this->error = _("Invalid input data");
767 return false;
768 }
769 if (empty($userdata['firstname']) && empty($userdata['lastname'])) {
770 $this->error = _("Name is missing");
771 return false;
772 }
773 if (empty($userdata['email'])) {
774 $this->error = _("E-mail address is missing");
775 return false;
776 }
62f7daa5 777
81fa4801 778 if (eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
779 $this->error = _("Nickname contains illegal characters");
780 return false;
781 }
62f7daa5 782
81fa4801 783 if (empty($userdata['nickname'])) {
784 $userdata['nickname'] = $userdata['email'];
785 }
62f7daa5 786
787 /* Check that specified backend is writable */
81fa4801 788 if (!$this->backends[$bnum]->writeable) {
789 $this->error = _("Addressbook is read-only");;
790 return false;
791 }
62f7daa5 792
81fa4801 793 /* Modify user in backend */
794 $res = $this->backends[$bnum]->modify($alias, $userdata);
795 if ($res) {
796 return $bnum;
797 } else {
0493ed11 798 $this->error = htmlspecialchars($this->backends[$bnum]->error);
81fa4801 799 return false;
800 }
62f7daa5 801
81fa4801 802 return FALSE; /* Not reached */
803 } /* end of modify() */
62f7daa5 804
805
81fa4801 806} /* End of class Addressbook */
807
8f6f9ba5 808/**
81fa4801 809 * Generic backend that all other backends extend
8f6f9ba5 810 * @package squirrelmail
c1ac62d4 811 * @subpackage addressbook
81fa4801 812 */
813class addressbook_backend {
814
815 /* Variables that all backends must provide. */
4272758c 816 /**
817 * Backend type
818 *
819 * Can be 'local' or 'remote'
820 * @var string backend type
821 */
81fa4801 822 var $btype = 'dummy';
4272758c 823 /**
824 * Internal backend name
825 * @var string
826 */
81fa4801 827 var $bname = 'dummy';
4272758c 828 /**
829 * Displayed backend name
830 * @var string
831 */
81fa4801 832 var $sname = 'Dummy backend';
62f7daa5 833
81fa4801 834 /*
835 * Variables common for all backends, but that
836 * should not be changed by the backends.
837 */
4272758c 838 /**
839 * Backend number
840 * @var integer
841 */
81fa4801 842 var $bnum = -1;
4272758c 843 /**
844 * Error messages
845 * @var string
846 */
81fa4801 847 var $error = '';
4272758c 848 /**
849 * Writeable flag
850 * @var bool
851 */
81fa4801 852 var $writeable = false;
62f7daa5 853
4272758c 854 /**
855 * Set error message
856 * @param string $string error message
857 * @return bool
858 */
81fa4801 859 function set_error($string) {
860 $this->error = '[' . $this->sname . '] ' . $string;
861 return false;
862 }
62f7daa5 863
864
81fa4801 865 /* ========================== Public ======================== */
62f7daa5 866
4272758c 867 /**
868 * Search for entries in backend
869 * @param string $expression
870 * @return bool
871 */
81fa4801 872 function search($expression) {
873 $this->set_error('search not implemented');
874 return false;
875 }
62f7daa5 876
4272758c 877 /**
878 * Find entry in backend by alias
879 * @param string $alias name used for id
880 * @return bool
881 */
81fa4801 882 function lookup($alias) {
883 $this->set_error('lookup not implemented');
884 return false;
a10110a5 885 }
62f7daa5 886
4272758c 887 /**
888 * List all entries in backend
889 * @return bool
890 */
81fa4801 891 function list_addr() {
892 $this->set_error('list_addr not implemented');
893 return false;
894 }
62f7daa5 895
4272758c 896 /**
897 * Add entry to backend
898 * @param array userdata
899 * @return bool
900 */
81fa4801 901 function add($userdata) {
902 $this->set_error('add not implemented');
903 return false;
904 }
62f7daa5 905
4272758c 906 /**
907 * Remove entry from backend
908 * @param string $alias name used for id
909 * @return bool
910 */
81fa4801 911 function remove($alias) {
912 $this->set_error('delete not implemented');
913 return false;
914 }
62f7daa5 915
4272758c 916 /**
917 * Modify entry in backend
918 * @param string $alias name used for id
919 * @param array $newuserdata new data
920 * @return bool
921 */
81fa4801 922 function modify($alias, $newuserdata) {
923 $this->set_error('modify not implemented');
924 return false;
925 }
81fa4801 926}
927
0419106e 928/*
929 PHP 5 requires that the class be made first, which seems rather
930 logical, and should have been the way it was generated the first time.
931*/
932
933require_once(SM_PATH . 'functions/abook_local_file.php');
934require_once(SM_PATH . 'functions/abook_ldap_server.php');
935
0419106e 936/* Only load database backend if database is configured */
62f7daa5 937if((isset($addrbook_dsn) && !empty($addrbook_dsn)) ||
7390e240 938 (isset($addrbook_global_dsn) && !empty($addrbook_global_dsn))) {
939 include_once(SM_PATH . 'functions/abook_database.php');
0419106e 940}
941
df788686 942/*
943 * hook allows adding different address book classes.
944 * class must follow address book class coding standards.
945 *
946 * see addressbook_backend class and functions/abook_*.php files.
7390e240 947 * @since 1.5.1 and 1.4.5
df788686 948 */
949do_hook('abook_add_class');
0419106e 950
796f91d9 951?>