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