db_prefs.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. <?php
  2. /**
  3. * db_prefs.php
  4. *
  5. * This contains functions for manipulating user preferences
  6. * stored in a database, accessed though the Pear DB layer.
  7. *
  8. * Database:
  9. *
  10. * The preferences table should have three columns:
  11. * user char \ primary
  12. * prefkey char / key
  13. * prefval blob
  14. *
  15. * CREATE TABLE userprefs (user CHAR(128) NOT NULL DEFAULT '',
  16. * prefkey CHAR(64) NOT NULL DEFAULT '',
  17. * prefval BLOB NOT NULL DEFAULT '',
  18. * primary key (user,prefkey));
  19. *
  20. * Configuration of databasename, username and password is done
  21. * by using conf.pl or the administrator plugin
  22. *
  23. * @copyright &copy; 1999-2007 The SquirrelMail Project Team
  24. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  25. * @version $Id$
  26. * @package squirrelmail
  27. * @subpackage prefs
  28. * @since 1.1.3
  29. */
  30. /** @ignore */
  31. if (!defined('SM_PATH')) define('SM_PATH','../');
  32. /** Unknown database */
  33. define('SMDB_UNKNOWN', 0);
  34. /** MySQL */
  35. define('SMDB_MYSQL', 1);
  36. /** PostgreSQL */
  37. define('SMDB_PGSQL', 2);
  38. /**
  39. * don't display errors (no code execution in functions/*.php).
  40. * will handle error in dbPrefs class.
  41. */
  42. @include_once('DB.php');
  43. global $prefs_are_cached, $prefs_cache;
  44. /**
  45. * @ignore
  46. */
  47. function cachePrefValues($username) {
  48. global $prefs_are_cached, $prefs_cache;
  49. sqgetGlobalVar('prefs_are_cached', $prefs_are_cached, SQ_SESSION );
  50. if ($prefs_are_cached) {
  51. sqgetGlobalVar('prefs_cache', $prefs_cache, SQ_SESSION );
  52. return;
  53. }
  54. sqsession_unregister('prefs_cache');
  55. sqsession_unregister('prefs_are_cached');
  56. $db = new dbPrefs;
  57. if(isset($db->error)) {
  58. printf( _("Preference database error (%s). Exiting abnormally"),
  59. $db->error);
  60. exit;
  61. }
  62. $db->fillPrefsCache($username);
  63. if (isset($db->error)) {
  64. printf( _("Preference database error (%s). Exiting abnormally"),
  65. $db->error);
  66. exit;
  67. }
  68. $prefs_are_cached = true;
  69. sqsession_register($prefs_cache, 'prefs_cache');
  70. sqsession_register($prefs_are_cached, 'prefs_are_cached');
  71. }
  72. /**
  73. * Class used to handle connections to prefs database and operations with preferences
  74. *
  75. * @package squirrelmail
  76. * @subpackage prefs
  77. * @since 1.1.3
  78. *
  79. */
  80. class dbPrefs {
  81. /**
  82. * Table used to store preferences
  83. * @var string
  84. */
  85. var $table = 'userprefs';
  86. /**
  87. * Field used to store owner of preference
  88. * @var string
  89. */
  90. var $user_field = 'user';
  91. /**
  92. * Field used to store preference name
  93. * @var string
  94. */
  95. var $key_field = 'prefkey';
  96. /**
  97. * Field used to store preference value
  98. * @var string
  99. */
  100. var $val_field = 'prefval';
  101. /**
  102. * Database connection object
  103. * @var object
  104. */
  105. var $dbh = NULL;
  106. /**
  107. * Error messages
  108. * @var string
  109. */
  110. var $error = NULL;
  111. /**
  112. * Database type (SMDB_* constants)
  113. * Is used in setKey().
  114. * @var integer
  115. */
  116. var $db_type = SMDB_UNKNOWN;
  117. /**
  118. * Default preferences
  119. * @var array
  120. */
  121. var $default = Array('theme_default' => 0,
  122. 'show_html_default' => '0');
  123. /**
  124. * Preference owner field size
  125. * @var integer
  126. * @since 1.5.1
  127. */
  128. var $user_size = 128;
  129. /**
  130. * Preference key field size
  131. * @var integer
  132. * @since 1.5.1
  133. */
  134. var $key_size = 64;
  135. /**
  136. * Preference value field size
  137. * @var integer
  138. * @since 1.5.1
  139. */
  140. var $val_size = 65536;
  141. /**
  142. * initialize the default preferences array.
  143. *
  144. */
  145. function dbPrefs() {
  146. // Try and read the default preferences file.
  147. $default_pref = SM_PATH . 'config/default_pref';
  148. if (@file_exists($default_pref)) {
  149. if ($file = @fopen($default_pref, 'r')) {
  150. while (!feof($file)) {
  151. $pref = fgets($file, 1024);
  152. $i = strpos($pref, '=');
  153. if ($i > 0) {
  154. $this->default[trim(substr($pref, 0, $i))] = trim(substr($pref, $i + 1));
  155. }
  156. }
  157. fclose($file);
  158. }
  159. }
  160. }
  161. /**
  162. * initialize DB connection object
  163. *
  164. * @return boolean true, if object is initialized
  165. *
  166. */
  167. function open() {
  168. global $prefs_dsn, $prefs_table;
  169. global $prefs_user_field, $prefs_key_field, $prefs_val_field;
  170. global $prefs_user_size, $prefs_key_size, $prefs_val_size;
  171. /* test if Pear DB class is available and freak out if it is not */
  172. if (! class_exists('DB')) {
  173. // same error also in abook_database.php
  174. $this->error = _("Could not include PEAR database functions required for the database backend.") . "\n";
  175. $this->error .= sprintf(_("Is PEAR installed, and is the include path set correctly to find %s?"),
  176. 'DB.php') . "\n";
  177. $this->error .= _("Please contact your system administrator and report this error.");
  178. return false;
  179. }
  180. if(isset($this->dbh)) {
  181. return true;
  182. }
  183. if (preg_match('/^mysql/', $prefs_dsn)) {
  184. $this->db_type = SMDB_MYSQL;
  185. } elseif (preg_match('/^pgsql/', $prefs_dsn)) {
  186. $this->db_type = SMDB_PGSQL;
  187. }
  188. if (!empty($prefs_table)) {
  189. $this->table = $prefs_table;
  190. }
  191. if (!empty($prefs_user_field)) {
  192. $this->user_field = $prefs_user_field;
  193. }
  194. if (!empty($prefs_key_field)) {
  195. $this->key_field = $prefs_key_field;
  196. }
  197. if (!empty($prefs_val_field)) {
  198. $this->val_field = $prefs_val_field;
  199. }
  200. if (!empty($prefs_user_size)) {
  201. $this->user_size = (int) $prefs_user_size;
  202. }
  203. if (!empty($prefs_key_size)) {
  204. $this->key_size = (int) $prefs_key_size;
  205. }
  206. if (!empty($prefs_val_size)) {
  207. $this->val_size = (int) $prefs_val_size;
  208. }
  209. $dbh = DB::connect($prefs_dsn, true);
  210. if(DB::isError($dbh)) {
  211. $this->error = DB::errorMessage($dbh);
  212. return false;
  213. }
  214. $this->dbh = $dbh;
  215. return true;
  216. }
  217. /**
  218. * Function used to handle database connection errors
  219. *
  220. * @param object PEAR Error object
  221. *
  222. */
  223. function failQuery($res = NULL) {
  224. if($res == NULL) {
  225. printf(_("Preference database error (%s). Exiting abnormally"),
  226. $this->error);
  227. } else {
  228. printf(_("Preference database error (%s). Exiting abnormally"),
  229. DB::errorMessage($res));
  230. }
  231. exit;
  232. }
  233. /**
  234. * Get user's prefs setting
  235. *
  236. * @param string $user user name
  237. * @param string $key preference name
  238. * @param mixed $default (since 1.2.5) default value
  239. *
  240. * @return mixed preference value
  241. *
  242. */
  243. function getKey($user, $key, $default = '') {
  244. global $prefs_cache;
  245. $temp = array(&$user, &$key);
  246. $result = do_hook('get_pref_override', $temp);
  247. if (is_null($result)) {
  248. cachePrefValues($user);
  249. if (isset($prefs_cache[$key])) {
  250. $result = $prefs_cache[$key];
  251. } else {
  252. //FIXME: is there a justification for having two prefs hooks so close? who uses them?
  253. $temp = array(&$user, &$key);
  254. $result = do_hook('get_pref', $temp);
  255. if (is_null($result)) {
  256. if (isset($this->default[$key])) {
  257. $result = $this->default[$key];
  258. } else {
  259. $result = $default;
  260. }
  261. }
  262. }
  263. }
  264. return $result;
  265. }
  266. /**
  267. * Delete user's prefs setting
  268. *
  269. * @param string $user user name
  270. * @param string $key preference name
  271. *
  272. * @return boolean
  273. *
  274. */
  275. function deleteKey($user, $key) {
  276. global $prefs_cache;
  277. if (!$this->open()) {
  278. return false;
  279. }
  280. $query = sprintf("DELETE FROM %s WHERE %s='%s' AND %s='%s'",
  281. $this->table,
  282. $this->user_field,
  283. $this->dbh->quoteString($user),
  284. $this->key_field,
  285. $this->dbh->quoteString($key));
  286. $res = $this->dbh->simpleQuery($query);
  287. if(DB::isError($res)) {
  288. $this->failQuery($res);
  289. }
  290. unset($prefs_cache[$key]);
  291. return true;
  292. }
  293. /**
  294. * Set user's preference
  295. *
  296. * @param string $user user name
  297. * @param string $key preference name
  298. * @param mixed $value preference value
  299. *
  300. * @return boolean
  301. *
  302. */
  303. function setKey($user, $key, $value) {
  304. if (!$this->open()) {
  305. return false;
  306. }
  307. /**
  308. * Check if username fits into db field
  309. */
  310. if (strlen($user) > $this->user_size) {
  311. $this->error = "Oversized username value."
  312. ." Your preferences can't be saved."
  313. ." See the administrator's manual or contact your system administrator.";
  314. /**
  315. * Debugging function. Can be used to log all issues that trigger
  316. * oversized field errors. Function should be enabled in all three
  317. * strlen checks. See http://www.php.net/error-log
  318. */
  319. // error_log($user.'|'.$key.'|'.$value."\n",3,'/tmp/oversized_log');
  320. // error is fatal
  321. $this->failQuery(null);
  322. }
  323. /**
  324. * Check if preference key fits into db field
  325. */
  326. if (strlen($key) > $this->key_size) {
  327. $err_msg = "Oversized user's preference key."
  328. ." Some preferences were not saved."
  329. ." See the administrator's manual or contact your system administrator.";
  330. // error is not fatal. Only some preference is not saved.
  331. trigger_error($err_msg,E_USER_WARNING);
  332. return false;
  333. }
  334. /**
  335. * Check if preference value fits into db field
  336. */
  337. if (strlen($value) > $this->val_size) {
  338. $err_msg = "Oversized user's preference value."
  339. ." Some preferences were not saved."
  340. ." See the administrator's manual or contact your system administrator.";
  341. // error is not fatal. Only some preference is not saved.
  342. trigger_error($err_msg,E_USER_WARNING);
  343. return false;
  344. }
  345. if ($this->db_type == SMDB_MYSQL) {
  346. $query = sprintf("REPLACE INTO %s (%s, %s, %s) ".
  347. "VALUES('%s','%s','%s')",
  348. $this->table,
  349. $this->user_field,
  350. $this->key_field,
  351. $this->val_field,
  352. $this->dbh->quoteString($user),
  353. $this->dbh->quoteString($key),
  354. $this->dbh->quoteString($value));
  355. $res = $this->dbh->simpleQuery($query);
  356. if(DB::isError($res)) {
  357. $this->failQuery($res);
  358. }
  359. } elseif ($this->db_type == SMDB_PGSQL) {
  360. $this->dbh->simpleQuery("BEGIN TRANSACTION");
  361. $query = sprintf("DELETE FROM %s WHERE %s='%s' AND %s='%s'",
  362. $this->table,
  363. $this->user_field,
  364. $this->dbh->quoteString($user),
  365. $this->key_field,
  366. $this->dbh->quoteString($key));
  367. $res = $this->dbh->simpleQuery($query);
  368. if (DB::isError($res)) {
  369. $this->dbh->simpleQuery("ROLLBACK TRANSACTION");
  370. $this->failQuery($res);
  371. }
  372. $query = sprintf("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
  373. $this->table,
  374. $this->user_field,
  375. $this->key_field,
  376. $this->val_field,
  377. $this->dbh->quoteString($user),
  378. $this->dbh->quoteString($key),
  379. $this->dbh->quoteString($value));
  380. $res = $this->dbh->simpleQuery($query);
  381. if (DB::isError($res)) {
  382. $this->dbh->simpleQuery("ROLLBACK TRANSACTION");
  383. $this->failQuery($res);
  384. }
  385. $this->dbh->simpleQuery("COMMIT TRANSACTION");
  386. } else {
  387. $query = sprintf("DELETE FROM %s WHERE %s='%s' AND %s='%s'",
  388. $this->table,
  389. $this->user_field,
  390. $this->dbh->quoteString($user),
  391. $this->key_field,
  392. $this->dbh->quoteString($key));
  393. $res = $this->dbh->simpleQuery($query);
  394. if (DB::isError($res)) {
  395. $this->failQuery($res);
  396. }
  397. $query = sprintf("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
  398. $this->table,
  399. $this->user_field,
  400. $this->key_field,
  401. $this->val_field,
  402. $this->dbh->quoteString($user),
  403. $this->dbh->quoteString($key),
  404. $this->dbh->quoteString($value));
  405. $res = $this->dbh->simpleQuery($query);
  406. if (DB::isError($res)) {
  407. $this->failQuery($res);
  408. }
  409. }
  410. return true;
  411. }
  412. /**
  413. * Fill preference cache array
  414. *
  415. * @param string $user user name
  416. *
  417. * @since 1.2.3
  418. *
  419. */
  420. function fillPrefsCache($user) {
  421. global $prefs_cache;
  422. if (!$this->open()) {
  423. return;
  424. }
  425. $prefs_cache = array();
  426. $query = sprintf("SELECT %s as prefkey, %s as prefval FROM %s ".
  427. "WHERE %s = '%s'",
  428. $this->key_field,
  429. $this->val_field,
  430. $this->table,
  431. $this->user_field,
  432. $this->dbh->quoteString($user));
  433. $res = $this->dbh->query($query);
  434. if (DB::isError($res)) {
  435. $this->failQuery($res);
  436. }
  437. while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
  438. $prefs_cache[$row['prefkey']] = $row['prefval'];
  439. }
  440. }
  441. } /* end class dbPrefs */
  442. /**
  443. * Returns the value for the requested preference
  444. * @ignore
  445. */
  446. function getPref($data_dir, $username, $pref_name, $default = '') {
  447. $db = new dbPrefs;
  448. if(isset($db->error)) {
  449. printf( _("Preference database error (%s). Exiting abnormally"),
  450. $db->error);
  451. exit;
  452. }
  453. return $db->getKey($username, $pref_name, $default);
  454. }
  455. /**
  456. * Remove the desired preference setting ($pref_name)
  457. * @ignore
  458. */
  459. function removePref($data_dir, $username, $pref_name) {
  460. global $prefs_cache;
  461. $db = new dbPrefs;
  462. if(isset($db->error)) {
  463. $db->failQuery();
  464. }
  465. $db->deleteKey($username, $pref_name);
  466. if (isset($prefs_cache[$pref_name])) {
  467. unset($prefs_cache[$pref_name]);
  468. }
  469. sqsession_register($prefs_cache , 'prefs_cache');
  470. return;
  471. }
  472. /**
  473. * Sets the desired preference setting ($pref_name) to whatever is in $value
  474. * @ignore
  475. */
  476. function setPref($data_dir, $username, $pref_name, $value) {
  477. global $prefs_cache;
  478. if (isset($prefs_cache[$pref_name]) && ($prefs_cache[$pref_name] == $value)) {
  479. return;
  480. }
  481. if ($value === '') {
  482. removePref($data_dir, $username, $pref_name);
  483. return;
  484. }
  485. $db = new dbPrefs;
  486. if(isset($db->error)) {
  487. $db->failQuery();
  488. }
  489. $db->setKey($username, $pref_name, $value);
  490. $prefs_cache[$pref_name] = $value;
  491. assert_options(ASSERT_ACTIVE, 1);
  492. assert_options(ASSERT_BAIL, 1);
  493. assert ('$value == $prefs_cache[$pref_name]');
  494. sqsession_register($prefs_cache , 'prefs_cache');
  495. return;
  496. }
  497. /**
  498. * This checks if the prefs are available
  499. * @ignore
  500. */
  501. function checkForPrefs($data_dir, $username) {
  502. $db = new dbPrefs;
  503. if(isset($db->error)) {
  504. $db->failQuery();
  505. }
  506. }
  507. /**
  508. * Writes the Signature
  509. * @ignore
  510. */
  511. function setSig($data_dir, $username, $number, $value) {
  512. if ($number == "g") {
  513. $key = '___signature___';
  514. } else {
  515. $key = sprintf('___sig%s___', $number);
  516. }
  517. setPref($data_dir, $username, $key, $value);
  518. return;
  519. }
  520. /**
  521. * Gets the signature
  522. * @ignore
  523. */
  524. function getSig($data_dir, $username, $number) {
  525. if ($number == "g") {
  526. $key = '___signature___';
  527. } else {
  528. $key = sprintf('___sig%d___', $number);
  529. }
  530. return getPref($data_dir, $username, $key);
  531. }