Code cleanup brigage...
[squirrelmail.git] / functions / addressbook.php
CommitLineData
5100704d 1<?php
2
35586184 3/**
4 * addressbook.php
5 *
6 * Copyright (c) 1999-2001 The Squirrelmail Development Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
9 * Functions and classes for the addressbook system.
10 *
11 * $Id$
12 */
13
14/*****************************************************************/
15/*** THIS FILE NEEDS TO HAVE ITS FORMATTING FIXED!!! ***/
16/*** PLEASE DO SO AND REMOVE THIS COMMENT SECTION. ***/
17/*** + Base level indent should begin at left margin, as ***/
18/*** the require_once below looks. ***/
19/*** + All identation should consist of four space blocks ***/
20/*** + Tab characters are evil. ***/
21/*** + all comments should use "slash-star ... star-slash" ***/
22/*** style -- no pound characters, no slash-slash style ***/
23/*** + FLOW CONTROL STATEMENTS (if, while, etc) SHOULD ***/
24/*** ALWAYS USE { AND } CHARACTERS!!! ***/
25/*** + Please use ' instead of ", when possible. Note " ***/
26/*** should always be used in _( ) function calls. ***/
27/*** Thank you for your help making the SM code more readable. ***/
28/*****************************************************************/
29
30// This is the path to the global site-wide addressbook.
31// It looks and feels just like a user's .abook file
32// If this is in the data directory, use "$data_dir/global.abook"
33// If not, specify the path as though it was accessed from the
34// src/ directory ("../global.abook" -> in main directory)
35//
36// If you don't want a global site-wide addressbook, comment these
37// two lines out. (They are disabled by default.)
38//
39// The global addressbook is unmodifiable by anyone. You must actually
40// use a shell script or whatnot to modify the contents.
41//
42//global $data_dir;
43//$address_book_global_filename = "$data_dir/global.abook";
44
45// Include backends here.
46require_once('../functions/abook_local_file.php');
47require_once('../functions/abook_ldap_server.php');
5100704d 48
01589f26 49 // Use this if you wanna have a global address book
50 if (isset($address_book_global_filename))
0fc2aca0 51 include_once('../functions/abook_global_file.php');
01589f26 52
6ff1c690 53 // Only load database backend if database is configured
66dbe1d4 54 global $addrbook_dsn;
6ff1c690 55 if(isset($addrbook_dsn))
0fc2aca0 56 include_once('../functions/abook_database.php');
8f583e40 57
0515b57a 58
5100704d 59 // Create and initialize an addressbook object.
60 // Returns the created object
0d273153 61 function addressbook_init($showerr = true, $onlylocal = false) {
01589f26 62 global $data_dir, $username, $ldap_server, $address_book_global_filename;
6ff1c690 63 global $addrbook_dsn;
5100704d 64
65 // Create a new addressbook object
66 $abook = new AddressBook;
67
6ff1c690 68 // Always add a local backend. We use *either* file-based *or* a
69 // database addressbook. If $addrbook_dsn is set, the database
70 // backend is used. If not, addressbooks are stores in files.
71 if(isset($addrbook_dsn) && !empty($addrbook_dsn)) {
72 // Database
73 $r = $abook->add_backend('database', Array('dsn' => $addrbook_dsn,
74 'owner' => $username,
75 'table' => 'address'));
76 if(!$r && $showerr) {
77 printf(_("Error initializing addressbook database."));
78 exit;
79 }
80 } else {
81 // File
82 $filename = sprintf('%s%s.abook', $data_dir, $username);
83 $r = $abook->add_backend('local_file', Array('filename' => $filename,
84 'create' => true));
85 if(!$r && $showerr) {
86 printf(_("Error opening file %s"), $filename);
87 exit;
88 }
01589f26 89
90 }
91
92 // This would be for the global addressbook
93 if (isset($address_book_global_filename)) {
94 $r = $abook->add_backend('global_file');
95 if (!$r && $showerr) {
96 printf(_("Error initializing global addressbook."));
97 exit;
98 }
5100704d 99 }
0d273153 100
101 if($onlylocal)
102 return $abook;
5100704d 103
95a4d303 104 // Load configured LDAP servers (if PHP has LDAP support)
6aff2ff6 105 if(isset($ldap_server) && is_array($ldap_server) &&
106 function_exists('ldap_connect')) {
1ed2b1e0 107 reset($ldap_server);
fb16d219 108 while(list($undef,$param) = each($ldap_server)) {
1ed2b1e0 109 if(is_array($param)) {
b9bfd165 110 $r = $abook->add_backend('ldap_server', $param);
1ed2b1e0 111 if(!$r && $showerr) {
b9bfd165 112 printf('&nbsp;' . _("Error initializing LDAP server %s:") .
113 "<BR>\n", $param['host']);
114 print('&nbsp;' . $abook->error);
1ed2b1e0 115 exit;
116 }
117 }
118 }
799b9070 119 }
5100704d 120
121 // Return the initialized object
122 return $abook;
123 }
124
0515b57a 125
126 // Had to move this function outside of the Addressbook Class
127 // PHP 4.0.4 Seemed to be having problems with inline functions.
485599c7 128 function addressbook_cmp($a,$b) {
b9bfd165 129 if($a['backend'] > $b['backend'])
0515b57a 130 return 1;
b9bfd165 131 else if($a['backend'] < $b['backend'])
0515b57a 132 return -1;
b9bfd165 133 return (strtolower($a['name']) > strtolower($b['name'])) ? 1 : -1;
0515b57a 134 }
5100704d 135
136
137 /**
138 ** This is the main address book class that connect all the
139 ** backends and provide services to the functions above.
140 **
141 **/
0515b57a 142
5100704d 143 class AddressBook {
144 var $backends = array();
145 var $numbackends = 0;
b9bfd165 146 var $error = '';
ce916cdf 147 var $localbackend = 0;
b9bfd165 148 var $localbackendname = '';
5100704d 149
150 // Constructor function.
151 function AddressBook() {
1ed2b1e0 152 $localbackendname = _("Personal address book");
5100704d 153 }
154
155 // Return an array of backends of a given type,
156 // or all backends if no type is specified.
b9bfd165 157 function get_backend_list($type = '') {
5100704d 158 $ret = array();
159 for($i = 1 ; $i <= $this->numbackends ; $i++) {
160 if(empty($type) || $type == $this->backends[$i]->btype) {
507832e7 161 $ret[] = &$this->backends[$i];
5100704d 162 }
163 }
164 return $ret;
165 }
166
167
168 // ========================== Public ========================
169
170 // Add a new backend. $backend is the name of a backend
171 // (without the abook_ prefix), and $param is an optional
172 // mixed variable that is passed to the backend constructor.
173 // See each of the backend classes for valid parameters.
b9bfd165 174 function add_backend($backend, $param = '') {
175 $backend_name = 'abook_' . $backend;
485599c7 176 eval('$newback = new ' . $backend_name . '($param);');
5100704d 177 if(!empty($newback->error)) {
178 $this->error = $newback->error;
179 return false;
180 }
181
182 $this->numbackends++;
183
184 $newback->bnum = $this->numbackends;
185 $this->backends[$this->numbackends] = $newback;
ce916cdf 186
187 // Store ID of first local backend added
b9bfd165 188 if($this->localbackend == 0 && $newback->btype == 'local') {
ce916cdf 189 $this->localbackend = $this->numbackends;
1ed2b1e0 190 $this->localbackendname = $newback->sname;
191 }
ce916cdf 192
5100704d 193 return $this->numbackends;
194 }
195
196
197 // Return a list of addresses matching expression in
198 // all backends of a given type.
2f73dc15 199 function search($expression, $bnum = -1) {
5100704d 200 $ret = array();
b9bfd165 201 $this->error = '';
5100704d 202
2f73dc15 203 // Search all backends
204 if($bnum == -1) {
b9bfd165 205 $sel = $this->get_backend_list('');
2f73dc15 206 $failed = 0;
207 for($i = 0 ; $i < sizeof($sel) ; $i++) {
208 $backend = &$sel[$i];
b9bfd165 209 $backend->error = '';
2f73dc15 210 $res = $backend->search($expression);
211 if(is_array($res)) {
212 $ret = array_merge($ret, $res);
213 } else {
b9bfd165 214 $this->error .= "<br>\n" . $backend->error;
2f73dc15 215 $failed++;
216 }
5100704d 217 }
2f73dc15 218
219 // Only fail if all backends failed
220 if($failed >= sizeof($sel))
221 return false;
222
5100704d 223 }
224
2f73dc15 225 // Search only one backend
226 else {
227 $ret = $this->backends[$bnum]->search($expression);
228 if(!is_array($ret)) {
b9bfd165 229 $this->error .= "<br>\n" . $this->backends[$bnum]->error;
2f73dc15 230 return false;
231 }
232 }
0d273153 233
5100704d 234 return $ret;
235 }
236
0515b57a 237
5100704d 238 // Return a sorted search
2f73dc15 239 function s_search($expression, $bnum = -1) {
0515b57a 240
241 $ret = $this->search($expression, $bnum);
242 if(!is_array($ret))
243 return $ret;
485599c7 244 usort($ret, 'addressbook_cmp');
0515b57a 245 return $ret;
5100704d 246 }
247
248
249 // Lookup an address by alias. Only possible in
250 // local backends.
1ed2b1e0 251 function lookup($alias, $bnum = -1) {
5100704d 252 $ret = array();
253
1ed2b1e0 254 if($bnum > -1) {
255 $res = $this->backends[$bnum]->lookup($alias);
256 if(is_array($res)) {
257 return $res;
258 } else {
259 $this->error = $backend->error;
260 return false;
261 }
262 }
263
b9bfd165 264 $sel = $this->get_backend_list('local');
5100704d 265 for($i = 0 ; $i < sizeof($sel) ; $i++) {
266 $backend = &$sel[$i];
b9bfd165 267 $backend->error = '';
5100704d 268 $res = $backend->lookup($alias);
269 if(is_array($res)) {
1ed2b1e0 270 if(!empty($res))
271 return $res;
5100704d 272 } else {
273 $this->error = $backend->error;
274 return false;
275 }
276 }
277
278 return $ret;
279 }
280
281
282 // Return all addresses
2f73dc15 283 function list_addr($bnum = -1) {
5100704d 284 $ret = array();
285
2f73dc15 286 if($bnum == -1)
b9bfd165 287 $sel = $this->get_backend_list('local');
2f73dc15 288 else
289 $sel = array(0 => &$this->backends[$bnum]);
290
5100704d 291 for($i = 0 ; $i < sizeof($sel) ; $i++) {
292 $backend = &$sel[$i];
b9bfd165 293 $backend->error = '';
5100704d 294 $res = $backend->list_addr();
295 if(is_array($res)) {
296 $ret = array_merge($ret, $res);
297 } else {
298 $this->error = $backend->error;
299 return false;
300 }
301 }
302
303 return $ret;
304 }
305
306
307 // Create a new address from $userdata, in backend $bnum.
308 // Return the backend number that the/ address was added
309 // to, or false if it failed.
310 function add($userdata, $bnum) {
311
312 // Validate data
313 if(!is_array($userdata)) {
314 $this->error = _("Invalid input data");
315 return false;
316 }
b9bfd165 317 if(empty($userdata['firstname']) &&
318 empty($userdata['lastname'])) {
5100704d 319 $this->error = _("Name is missing");
320 return false;
321 }
b9bfd165 322 if(empty($userdata['email'])) {
5100704d 323 $this->error = _("E-mail address is missing");
324 return false;
325 }
b9bfd165 326 if(empty($userdata['nickname'])) {
327 $userdata['nickname'] = $userdata['email'];
5100704d 328 }
329
74bd0763 330 if(eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
d4cbb1ef 331 $this->error = _("Nickname contains illegal characters");
1ed2b1e0 332 return false;
333 }
334
5100704d 335 // Check that specified backend accept new entries
336 if(!$this->backends[$bnum]->writeable) {
1e62e50e 337 $this->error = _("Addressbook is read-only");
5100704d 338 return false;
339 }
340
341 // Add address to backend
342 $res = $this->backends[$bnum]->add($userdata);
343 if($res) {
344 return $bnum;
345 } else {
346 $this->error = $this->backends[$bnum]->error;
347 return false;
348 }
349
350 return false; // Not reached
1ed2b1e0 351 } // end of add()
5100704d 352
1ed2b1e0 353
354 // Remove the user identified by $alias from backend $bnum
355 // If $alias is an array, all users in the array are removed.
356 function remove($alias, $bnum) {
357
358 // Check input
359 if(empty($alias))
360 return true;
361
362 // Convert string to single element array
363 if(!is_array($alias))
364 $alias = array(0 => $alias);
365
366 // Check that specified backend is writable
367 if(!$this->backends[$bnum]->writeable) {
368 $this->error = _("Addressbook is read-only");
369 return false;
370 }
371
372 // Remove user from backend
373 $res = $this->backends[$bnum]->remove($alias);
374 if($res) {
375 return $bnum;
376 } else {
377 $this->error = $this->backends[$bnum]->error;
378 return false;
379 }
380
381 return false; // Not reached
382 } // end of remove()
383
384
385 // Remove the user identified by $alias from backend $bnum
386 // If $alias is an array, all users in the array are removed.
387 function modify($alias, $userdata, $bnum) {
388
389 // Check input
390 if(empty($alias) || !is_string($alias))
391 return true;
392
393 // Validate data
394 if(!is_array($userdata)) {
395 $this->error = _("Invalid input data");
396 return false;
397 }
b9bfd165 398 if(empty($userdata['firstname']) &&
399 empty($userdata['lastname'])) {
1ed2b1e0 400 $this->error = _("Name is missing");
401 return false;
402 }
b9bfd165 403 if(empty($userdata['email'])) {
1ed2b1e0 404 $this->error = _("E-mail address is missing");
405 return false;
406 }
d9fdc8d3 407
485599c7 408 if(eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
d65734bd 409 $this->error = _("Nickname contains illegal characters");
d9fdc8d3 410 return false;
411 }
412
b9bfd165 413 if(empty($userdata['nickname'])) {
414 $userdata['nickname'] = $userdata['email'];
1ed2b1e0 415 }
416
417 // Check that specified backend is writable
418 if(!$this->backends[$bnum]->writeable) {
d9fdc8d3 419 $this->error = _("Addressbook is read-only");;
1ed2b1e0 420 return false;
421 }
422
423 // Modify user in backend
424 $res = $this->backends[$bnum]->modify($alias, $userdata);
425 if($res) {
426 return $bnum;
427 } else {
428 $this->error = $this->backends[$bnum]->error;
429 return false;
430 }
431
432 return false; // Not reached
433 } // end of modify()
434
5100704d 435
0515b57a 436 } // End of class Addressbook
5100704d 437
438 /**
439 ** Generic backend that all other backends extend
440 **/
441 class addressbook_backend {
442
443 // Variables that all backends must provide.
b9bfd165 444 var $btype = 'dummy';
445 var $bname = 'dummy';
446 var $sname = 'Dummy backend';
5100704d 447
448 // Variables common for all backends, but that
449 // should not be changed by the backends.
450 var $bnum = -1;
b9bfd165 451 var $error = '';
5100704d 452 var $writeable = false;
453
454 function set_error($string) {
b9bfd165 455 $this->error = '[' . $this->sname . '] ' . $string;
5100704d 456 return false;
457 }
458
459
460 // ========================== Public ========================
461
462 function search($expression) {
b9bfd165 463 $this->set_error('search not implemented');
5100704d 464 return false;
465 }
466
467 function lookup($alias) {
b9bfd165 468 $this->set_error('lookup not implemented');
5100704d 469 return false;
470 }
471
472 function list_addr() {
b9bfd165 473 $this->set_error('list_addr not implemented');
5100704d 474 return false;
475 }
476
477 function add($userdata) {
b9bfd165 478 $this->set_error('add not implemented');
5100704d 479 return false;
480 }
481
1ed2b1e0 482 function remove($alias) {
b9bfd165 483 $this->set_error('delete not implemented');
1ed2b1e0 484 return false;
485 }
486
487 function modify($alias, $newuserdata) {
b9bfd165 488 $this->set_error('modify not implemented');
1ed2b1e0 489 return false;
490 }
491
5100704d 492 }
493
d4cbb1ef 494?>