Better error reporting
[squirrelmail.git] / functions / abook_database.php
CommitLineData
9b9474d6 1<?php
4b4abf93 2
35586184 3/**
4 * abook_database.php
5 *
47ccfad4 6 * @copyright &copy; 1999-2006 The SquirrelMail Project Team
4b4abf93 7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
a9d318b0 8 * @version $Id$
d6c32258 9 * @package squirrelmail
a9d318b0 10 * @subpackage addressbook
35586184 11 */
d6c32258 12
62f7daa5 13/** Needs the DB functions */
c9fcea56 14if (!include_once('DB.php')) {
15 // same error also in db_prefs.php
16 require_once(SM_PATH . 'functions/display_messages.php');
17 $error = _("Could not include PEAR database functions required for the database backend.") . "<br />\n";
2ad4cea9 18 $error .= sprintf(_("Is PEAR installed, and is the include path set correctly to find %s?"),
19 '<tt>DB.php</tt>') . "<br />\n";
c9fcea56 20 $error .= _("Please contact your system administrator and report this error.");
21 error_box($error, $color);
17fca61d 22 exit;
c9fcea56 23}
d6c32258 24
25/**
0541c5ae 26 * Address book in a database backend
27 *
28 * Backend for personal/shared address book stored in a database,
29 * accessed using the DB-classes in PEAR.
30 *
31 * IMPORTANT: The PEAR modules must be in the include path
32 * for this class to work.
33 *
34 * An array with the following elements must be passed to
35 * the class constructor (elements marked ? are optional):
36 * <pre>
37 * dsn => database DNS (see PEAR for syntax)
38 * table => table to store addresses in (must exist)
39 * owner => current user (owner of address data)
40 * ? name => name of address book
41 * ? writeable => set writeable flag (true/false)
42 * ? listing => enable/disable listing
43 * </pre>
44 * The table used should have the following columns:
45 * owner, nickname, firstname, lastname, email, label
46 * The pair (owner,nickname) should be unique (primary key).
47 *
48 * NOTE. This class should not be used directly. Use the
49 * "AddressBook" class instead.
d6c32258 50 * @package squirrelmail
0541c5ae 51 * @subpackage addressbook
d6c32258 52 */
9b9474d6 53class abook_database extends addressbook_backend {
0541c5ae 54 /**
55 * Backend type
56 * @var string
57 */
9b9474d6 58 var $btype = 'local';
0541c5ae 59 /**
60 * Backend name
61 * @var string
62 */
9b9474d6 63 var $bname = 'database';
62f7daa5 64
0541c5ae 65 /**
66 * Data Source Name (connection description)
67 * @var string
68 */
9b9474d6 69 var $dsn = '';
0541c5ae 70 /**
71 * Table that stores addresses
72 * @var string
73 */
9b9474d6 74 var $table = '';
0541c5ae 75 /**
76 * Owner name
77 *
78 * Limits list of database entries visible to end user
79 * @var string
80 */
9b9474d6 81 var $owner = '';
0541c5ae 82 /**
83 * Database Handle
84 * @var resource
85 */
9b9474d6 86 var $dbh = false;
0541c5ae 87 /**
88 * Enable/disable writing into address book
89 * @var bool
90 */
9b9474d6 91 var $writeable = true;
0541c5ae 92 /**
93 * Enable/disable address book listing
94 * @var bool
95 */
96 var $listing = true;
62f7daa5 97
9b9474d6 98 /* ========================== Private ======================= */
62f7daa5 99
0541c5ae 100 /**
101 * Constructor
102 * @param array $param address book backend options
103 */
9b9474d6 104 function abook_database($param) {
105 $this->sname = _("Personal address book");
62f7daa5 106
9b9474d6 107 if (is_array($param)) {
62f7daa5 108 if (empty($param['dsn']) ||
109 empty($param['table']) ||
9b9474d6 110 empty($param['owner'])) {
111 return $this->set_error('Invalid parameters');
112 }
62f7daa5 113
91be2362 114 $this->dsn = $param['dsn'];
115 $this->table = $param['table'];
116 $this->owner = $param['owner'];
62f7daa5 117
9b9474d6 118 if (!empty($param['name'])) {
91be2362 119 $this->sname = $param['name'];
9b9474d6 120 }
7902aca2 121
9b9474d6 122 if (isset($param['writeable'])) {
91be2362 123 $this->writeable = $param['writeable'];
9b9474d6 124 }
7902aca2 125
30e9932c 126 if (isset($param['listing'])) {
127 $this->listing = $param['listing'];
128 }
129
7902aca2 130 $this->open(true);
9b9474d6 131 }
132 else {
91be2362 133 return $this->set_error('Invalid argument to constructor');
9b9474d6 134 }
135 }
62f7daa5 136
137
0541c5ae 138 /**
e50f5ac2 139 * Open the database.
0541c5ae 140 * @param bool $new new connection if it is true
141 * @return bool
142 */
9b9474d6 143 function open($new = false) {
144 $this->error = '';
62f7daa5 145
9b9474d6 146 /* Return true is file is open and $new is unset */
147 if ($this->dbh && !$new) {
7902aca2 148 return true;
9b9474d6 149 }
62f7daa5 150
9b9474d6 151 /* Close old file, if any */
152 if ($this->dbh) {
153 $this->close();
154 }
62f7daa5 155
9b9474d6 156 $dbh = DB::connect($this->dsn, true);
62f7daa5 157
286fe80b 158 if (DB::isError($dbh)) {
701c9c6b 159 return $this->set_error(sprintf(_("Database error: %s"),
7902aca2 160 DB::errorMessage($dbh)));
9b9474d6 161 }
62f7daa5 162
9b9474d6 163 $this->dbh = $dbh;
164 return true;
165 }
7902aca2 166
0541c5ae 167 /**
168 * Close the file and forget the filehandle
169 */
9b9474d6 170 function close() {
171 $this->dbh->disconnect();
172 $this->dbh = false;
173 }
7902aca2 174
9b9474d6 175 /* ========================== Public ======================== */
62f7daa5 176
0541c5ae 177 /**
178 * Search the database
179 * @param string $expr search expression
180 * @return array search results
181 */
aa8c4265 182 function search($expr) {
9b9474d6 183 $ret = array();
184 if(!$this->open()) {
7902aca2 185 return false;
9b9474d6 186 }
30e9932c 187
9b9474d6 188 /* To be replaced by advanded search expression parsing */
189 if (is_array($expr)) {
190 return;
191 }
192
327e2d96 193 // don't allow wide search when listing is disabled.
194 if ($expr=='*' && ! $this->listing)
195 return array();
196
9b9474d6 197 /* Make regexp from glob'ed expression */
198 $expr = str_replace('?', '_', $expr);
199 $expr = str_replace('*', '%', $expr);
200 $expr = $this->dbh->quoteString($expr);
201 $expr = "%$expr%";
202
203 $query = sprintf("SELECT * FROM %s WHERE owner='%s' AND " .
204 "(firstname LIKE '%s' OR lastname LIKE '%s')",
205 $this->table, $this->owner, $expr, $expr);
206 $res = $this->dbh->query($query);
207
208 if (DB::isError($res)) {
701c9c6b 209 return $this->set_error(sprintf(_("Database error: %s"),
7902aca2 210 DB::errorMessage($res)));
9b9474d6 211 }
7902aca2 212
9b9474d6 213 while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
91be2362 214 array_push($ret, array('nickname' => $row['nickname'],
215 'name' => "$row[firstname] $row[lastname]",
216 'firstname' => $row['firstname'],
217 'lastname' => $row['lastname'],
218 'email' => $row['email'],
219 'label' => $row['label'],
220 'backend' => $this->bnum,
221 'source' => &$this->sname));
9b9474d6 222 }
223 return $ret;
224 }
62f7daa5 225
0541c5ae 226 /**
227 * Lookup alias
228 * @param string $alias alias
229 * @return array search results
230 */
aa8c4265 231 function lookup($alias) {
9b9474d6 232 if (empty($alias)) {
7902aca2 233 return array();
9b9474d6 234 }
62f7daa5 235
9b9474d6 236 $alias = strtolower($alias);
7902aca2 237
9b9474d6 238 if (!$this->open()) {
7902aca2 239 return false;
9b9474d6 240 }
62f7daa5 241
2c90a9f2 242 $query = sprintf("SELECT * FROM %s WHERE owner='%s' AND LOWER(nickname)='%s'",
11f2b6ba 243 $this->table, $this->owner, $this->dbh->quoteString($alias));
7902aca2 244
9b9474d6 245 $res = $this->dbh->query($query);
7902aca2 246
9b9474d6 247 if (DB::isError($res)) {
701c9c6b 248 return $this->set_error(sprintf(_("Database error: %s"),
7902aca2 249 DB::errorMessage($res)));
9b9474d6 250 }
7902aca2 251
9b9474d6 252 if ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
91be2362 253 return array('nickname' => $row['nickname'],
254 'name' => "$row[firstname] $row[lastname]",
255 'firstname' => $row['firstname'],
256 'lastname' => $row['lastname'],
257 'email' => $row['email'],
258 'label' => $row['label'],
259 'backend' => $this->bnum,
260 'source' => &$this->sname);
9b9474d6 261 }
262 return array();
263 }
264
0541c5ae 265 /**
e50f5ac2 266 * List all addresses
0541c5ae 267 * @return array search results
268 */
aa8c4265 269 function list_addr() {
9b9474d6 270 $ret = array();
271 if (!$this->open()) {
7902aca2 272 return false;
9b9474d6 273 }
62f7daa5 274
91e0dccc 275 if(isset($this->listing) && !$this->listing) {
276 return array();
277 }
30e9932c 278
7902aca2 279
9b9474d6 280 $query = sprintf("SELECT * FROM %s WHERE owner='%s'",
281 $this->table, $this->owner);
7902aca2 282
9b9474d6 283 $res = $this->dbh->query($query);
62f7daa5 284
9b9474d6 285 if (DB::isError($res)) {
701c9c6b 286 return $this->set_error(sprintf(_("Database error: %s"),
7902aca2 287 DB::errorMessage($res)));
9b9474d6 288 }
7902aca2 289
9b9474d6 290 while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
91be2362 291 array_push($ret, array('nickname' => $row['nickname'],
292 'name' => "$row[firstname] $row[lastname]",
293 'firstname' => $row['firstname'],
294 'lastname' => $row['lastname'],
295 'email' => $row['email'],
296 'label' => $row['label'],
297 'backend' => $this->bnum,
298 'source' => &$this->sname));
9b9474d6 299 }
300 return $ret;
301 }
7902aca2 302
0541c5ae 303 /**
304 * Add address
305 * @param array $userdata added data
306 * @return bool
307 */
9b9474d6 308 function add($userdata) {
309 if (!$this->writeable) {
701c9c6b 310 return $this->set_error(_("Addressbook is read-only"));
9b9474d6 311 }
7902aca2 312
9b9474d6 313 if (!$this->open()) {
7902aca2 314 return false;
9b9474d6 315 }
62f7daa5 316
9b9474d6 317 /* See if user exist already */
318 $ret = $this->lookup($userdata['nickname']);
319 if (!empty($ret)) {
2706a0b1 320 return $this->set_error(sprintf(_("User \"%s\" already exists"),$ret['nickname']));
9b9474d6 321 }
322
323 /* Create query */
324 $query = sprintf("INSERT INTO %s (owner, nickname, firstname, " .
325 "lastname, email, label) VALUES('%s','%s','%s'," .
326 "'%s','%s','%s')",
327 $this->table, $this->owner,
328 $this->dbh->quoteString($userdata['nickname']),
62f7daa5 329 $this->dbh->quoteString($userdata['firstname']),
8419c13b 330 $this->dbh->quoteString((!empty($userdata['lastname'])?$userdata['lastname']:'')),
62f7daa5 331 $this->dbh->quoteString($userdata['email']),
8419c13b 332 $this->dbh->quoteString((!empty($userdata['label'])?$userdata['label']:'')) );
9b9474d6 333
334 /* Do the insert */
7902aca2 335 $r = $this->dbh->simpleQuery($query);
9b9474d6 336 if ($r == DB_OK) {
337 return true;
338 }
7902aca2 339
9b9474d6 340 /* Fail */
701c9c6b 341 return $this->set_error(sprintf(_("Database error: %s"),
7902aca2 342 DB::errorMessage($r)));
9b9474d6 343 }
7902aca2 344
0541c5ae 345 /**
346 * Delete address
347 * @param string $alias alias that has to be deleted
348 * @return bool
349 */
9b9474d6 350 function remove($alias) {
351 if (!$this->writeable) {
701c9c6b 352 return $this->set_error(_("Addressbook is read-only"));
9b9474d6 353 }
7902aca2 354
9b9474d6 355 if (!$this->open()) {
7902aca2 356 return false;
9b9474d6 357 }
62f7daa5 358
9b9474d6 359 /* Create query */
360 $query = sprintf("DELETE FROM %s WHERE owner='%s' AND (",
361 $this->table, $this->owner);
7902aca2 362
9b9474d6 363 $sepstr = '';
364 while (list($undef, $nickname) = each($alias)) {
16a973d7 365 $query .= sprintf("%s nickname='%s' ", $sepstr,
7902aca2 366 $this->dbh->quoteString($nickname));
91be2362 367 $sepstr = 'OR';
9b9474d6 368 }
369 $query .= ')';
7902aca2 370
9b9474d6 371 /* Delete entry */
372 $r = $this->dbh->simpleQuery($query);
373 if ($r == DB_OK) {
374 return true;
375 }
7902aca2 376
9b9474d6 377 /* Fail */
378 return $this->set_error(sprintf(_("Database error: %s"),
7902aca2 379 DB::errorMessage($r)));
9b9474d6 380 }
7902aca2 381
0541c5ae 382 /**
383 * Modify address
384 * @param string $alias modified alias
385 * @param array $userdata new data
386 * @return bool
387 */
9b9474d6 388 function modify($alias, $userdata) {
389 if (!$this->writeable) {
701c9c6b 390 return $this->set_error(_("Addressbook is read-only"));
9b9474d6 391 }
7902aca2 392
9b9474d6 393 if (!$this->open()) {
7902aca2 394 return false;
9b9474d6 395 }
62f7daa5 396
9b9474d6 397 /* See if user exist */
398 $ret = $this->lookup($alias);
399 if (empty($ret)) {
2706a0b1 400 return $this->set_error(sprintf(_("User \"%s\" does not exist"),$alias));
9b9474d6 401 }
402
403 /* Create query */
404 $query = sprintf("UPDATE %s SET nickname='%s', firstname='%s', ".
405 "lastname='%s', email='%s', label='%s' ".
406 "WHERE owner='%s' AND nickname='%s'",
62f7daa5 407 $this->table,
9b9474d6 408 $this->dbh->quoteString($userdata['nickname']),
62f7daa5 409 $this->dbh->quoteString($userdata['firstname']),
8419c13b 410 $this->dbh->quoteString((!empty($userdata['lastname'])?$userdata['lastname']:'')),
62f7daa5 411 $this->dbh->quoteString($userdata['email']),
8419c13b 412 $this->dbh->quoteString((!empty($userdata['label'])?$userdata['label']:'')),
9b9474d6 413 $this->owner,
414 $this->dbh->quoteString($alias) );
415
416 /* Do the insert */
417 $r = $this->dbh->simpleQuery($query);
418 if ($r == DB_OK) {
419 return true;
420 }
7902aca2 421
9b9474d6 422 /* Fail */
423 return $this->set_error(sprintf(_("Database error: %s"),
424 DB::errorMessage($r)));
425 }
426} /* End of class abook_database */
7902aca2 427
c9fcea56 428// vim: et ts=4
8419c13b 429?>