abook_database.php 13 KB

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