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