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. |
46 | require_once('../functions/abook_local_file.php'); |
47 | require_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 |
3392dc86 |
82 | $filename = getHashedFile($username, $data_dir, "$username.abook"); |
6ff1c690 |
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(' ' . _("Error initializing LDAP server %s:") . |
113 | "<BR>\n", $param['host']); |
114 | print(' ' . $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 | ?> |