db_prefs.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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 1999-2014 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. // the default user field is "user", which in PostgreSQL
  195. // is an identifier and causes errors if not escaped
  196. //
  197. if ($this->db_type == SMDB_PGSQL) {
  198. $this->user_field = '"' . $this->user_field . '"';
  199. }
  200. if (!empty($prefs_key_field)) {
  201. $this->key_field = $prefs_key_field;
  202. }
  203. if (!empty($prefs_val_field)) {
  204. $this->val_field = $prefs_val_field;
  205. }
  206. if (!empty($prefs_user_size)) {
  207. $this->user_size = (int) $prefs_user_size;
  208. }
  209. if (!empty($prefs_key_size)) {
  210. $this->key_size = (int) $prefs_key_size;
  211. }
  212. if (!empty($prefs_val_size)) {
  213. $this->val_size = (int) $prefs_val_size;
  214. }
  215. $dbh = DB::connect($prefs_dsn, true);
  216. if(DB::isError($dbh)) {
  217. $this->error = DB::errorMessage($dbh);
  218. return false;
  219. }
  220. $this->dbh = $dbh;
  221. return true;
  222. }
  223. /**
  224. * Function used to handle database connection errors
  225. *
  226. * @param object PEAR Error object
  227. *
  228. */
  229. function failQuery($res = NULL) {
  230. if($res == NULL) {
  231. printf(_("Preference database error (%s). Exiting abnormally"),
  232. $this->error);
  233. } else {
  234. printf(_("Preference database error (%s). Exiting abnormally"),
  235. DB::errorMessage($res));
  236. }
  237. exit;
  238. }
  239. /**
  240. * Get user's prefs setting
  241. *
  242. * @param string $user user name
  243. * @param string $key preference name
  244. * @param mixed $default (since 1.2.5) default value
  245. *
  246. * @return mixed preference value
  247. *
  248. */
  249. function getKey($user, $key, $default = '') {
  250. global $prefs_cache;
  251. $temp = array(&$user, &$key);
  252. $result = do_hook('get_pref_override', $temp);
  253. if (is_null($result)) {
  254. cachePrefValues($user);
  255. if (isset($prefs_cache[$key])) {
  256. $result = $prefs_cache[$key];
  257. } else {
  258. //FIXME: is there a justification for having two prefs hooks so close? who uses them?
  259. $temp = array(&$user, &$key);
  260. $result = do_hook('get_pref', $temp);
  261. if (is_null($result)) {
  262. if (isset($this->default[$key])) {
  263. $result = $this->default[$key];
  264. } else {
  265. $result = $default;
  266. }
  267. }
  268. }
  269. }
  270. return $result;
  271. }
  272. /**
  273. * Delete user's prefs setting
  274. *
  275. * @param string $user user name
  276. * @param string $key preference name
  277. *
  278. * @return boolean
  279. *
  280. */
  281. function deleteKey($user, $key) {
  282. global $prefs_cache;
  283. if (!$this->open()) {
  284. return false;
  285. }
  286. $query = sprintf("DELETE FROM %s WHERE %s='%s' AND %s='%s'",
  287. $this->table,
  288. $this->user_field,
  289. $this->dbh->quoteString($user),
  290. $this->key_field,
  291. $this->dbh->quoteString($key));
  292. $res = $this->dbh->simpleQuery($query);
  293. if(DB::isError($res)) {
  294. $this->failQuery($res);
  295. }
  296. unset($prefs_cache[$key]);
  297. return true;
  298. }
  299. /**
  300. * Set user's preference
  301. *
  302. * @param string $user user name
  303. * @param string $key preference name
  304. * @param mixed $value preference value
  305. *
  306. * @return boolean
  307. *
  308. */
  309. function setKey($user, $key, $value) {
  310. if (!$this->open()) {
  311. return false;
  312. }
  313. /**
  314. * Check if username fits into db field
  315. */
  316. if (strlen($user) > $this->user_size) {
  317. $this->error = "Oversized username value."
  318. ." Your preferences can't be saved."
  319. ." See the administrator's manual or contact your system administrator.";
  320. /**
  321. * Debugging function. Can be used to log all issues that trigger
  322. * oversized field errors. Function should be enabled in all three
  323. * strlen checks. See http://www.php.net/error-log
  324. */
  325. // error_log($user.'|'.$key.'|'.$value."\n",3,'/tmp/oversized_log');
  326. // error is fatal
  327. $this->failQuery(null);
  328. }
  329. /**
  330. * Check if preference key fits into db field
  331. */
  332. if (strlen($key) > $this->key_size) {
  333. $err_msg = "Oversized user's preference key."
  334. ." Some preferences were not saved."
  335. ." See the administrator's manual or contact your system administrator.";
  336. // error is not fatal. Only some preference is not saved.
  337. trigger_error($err_msg,E_USER_WARNING);
  338. return false;
  339. }
  340. /**
  341. * Check if preference value fits into db field
  342. */
  343. if (strlen($value) > $this->val_size) {
  344. $err_msg = "Oversized user's preference value."
  345. ." Some preferences were not saved."
  346. ." See the administrator's manual or contact your system administrator.";
  347. // error is not fatal. Only some preference is not saved.
  348. trigger_error($err_msg,E_USER_WARNING);
  349. return false;
  350. }
  351. if ($this->db_type == SMDB_MYSQL) {
  352. $query = sprintf("REPLACE INTO %s (%s, %s, %s) ".
  353. "VALUES('%s','%s','%s')",
  354. $this->table,
  355. $this->user_field,
  356. $this->key_field,
  357. $this->val_field,
  358. $this->dbh->quoteString($user),
  359. $this->dbh->quoteString($key),
  360. $this->dbh->quoteString($value));
  361. $res = $this->dbh->simpleQuery($query);
  362. if(DB::isError($res)) {
  363. $this->failQuery($res);
  364. }
  365. } elseif ($this->db_type == SMDB_PGSQL) {
  366. $this->dbh->simpleQuery("BEGIN TRANSACTION");
  367. $query = sprintf("DELETE FROM %s WHERE %s='%s' AND %s='%s'",
  368. $this->table,
  369. $this->user_field,
  370. $this->dbh->quoteString($user),
  371. $this->key_field,
  372. $this->dbh->quoteString($key));
  373. $res = $this->dbh->simpleQuery($query);
  374. if (DB::isError($res)) {
  375. $this->dbh->simpleQuery("ROLLBACK TRANSACTION");
  376. $this->failQuery($res);
  377. }
  378. $query = sprintf("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
  379. $this->table,
  380. $this->user_field,
  381. $this->key_field,
  382. $this->val_field,
  383. $this->dbh->quoteString($user),
  384. $this->dbh->quoteString($key),
  385. $this->dbh->quoteString($value));
  386. $res = $this->dbh->simpleQuery($query);
  387. if (DB::isError($res)) {
  388. $this->dbh->simpleQuery("ROLLBACK TRANSACTION");
  389. $this->failQuery($res);
  390. }
  391. $this->dbh->simpleQuery("COMMIT TRANSACTION");
  392. } else {
  393. $query = sprintf("DELETE FROM %s WHERE %s='%s' AND %s='%s'",
  394. $this->table,
  395. $this->user_field,
  396. $this->dbh->quoteString($user),
  397. $this->key_field,
  398. $this->dbh->quoteString($key));
  399. $res = $this->dbh->simpleQuery($query);
  400. if (DB::isError($res)) {
  401. $this->failQuery($res);
  402. }
  403. $query = sprintf("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
  404. $this->table,
  405. $this->user_field,
  406. $this->key_field,
  407. $this->val_field,
  408. $this->dbh->quoteString($user),
  409. $this->dbh->quoteString($key),
  410. $this->dbh->quoteString($value));
  411. $res = $this->dbh->simpleQuery($query);
  412. if (DB::isError($res)) {
  413. $this->failQuery($res);
  414. }
  415. }
  416. return true;
  417. }
  418. /**
  419. * Fill preference cache array
  420. *
  421. * @param string $user user name
  422. *
  423. * @since 1.2.3
  424. *
  425. */
  426. function fillPrefsCache($user) {
  427. global $prefs_cache;
  428. if (!$this->open()) {
  429. return;
  430. }
  431. $prefs_cache = array();
  432. $query = sprintf("SELECT %s as prefkey, %s as prefval FROM %s ".
  433. "WHERE %s = '%s'",
  434. $this->key_field,
  435. $this->val_field,
  436. $this->table,
  437. $this->user_field,
  438. $this->dbh->quoteString($user));
  439. $res = $this->dbh->query($query);
  440. if (DB::isError($res)) {
  441. $this->failQuery($res);
  442. }
  443. while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
  444. $prefs_cache[$row['prefkey']] = $row['prefval'];
  445. }
  446. }
  447. } /* end class dbPrefs */
  448. /**
  449. * Returns the value for the requested preference
  450. * @ignore
  451. */
  452. function getPref($data_dir, $username, $pref_name, $default = '') {
  453. $db = new dbPrefs;
  454. if(isset($db->error)) {
  455. printf( _("Preference database error (%s). Exiting abnormally"),
  456. $db->error);
  457. exit;
  458. }
  459. return $db->getKey($username, $pref_name, $default);
  460. }
  461. /**
  462. * Remove the desired preference setting ($pref_name)
  463. * @ignore
  464. */
  465. function removePref($data_dir, $username, $pref_name) {
  466. global $prefs_cache;
  467. $db = new dbPrefs;
  468. if(isset($db->error)) {
  469. $db->failQuery();
  470. }
  471. $db->deleteKey($username, $pref_name);
  472. if (isset($prefs_cache[$pref_name])) {
  473. unset($prefs_cache[$pref_name]);
  474. }
  475. sqsession_register($prefs_cache , 'prefs_cache');
  476. return;
  477. }
  478. /**
  479. * Sets the desired preference setting ($pref_name) to whatever is in $value
  480. * @ignore
  481. */
  482. function setPref($data_dir, $username, $pref_name, $value) {
  483. global $prefs_cache;
  484. if (isset($prefs_cache[$pref_name]) && ($prefs_cache[$pref_name] == $value)) {
  485. return;
  486. }
  487. if ($value === '') {
  488. removePref($data_dir, $username, $pref_name);
  489. return;
  490. }
  491. $db = new dbPrefs;
  492. if(isset($db->error)) {
  493. $db->failQuery();
  494. }
  495. $db->setKey($username, $pref_name, $value);
  496. $prefs_cache[$pref_name] = $value;
  497. assert_options(ASSERT_ACTIVE, 1);
  498. assert_options(ASSERT_BAIL, 1);
  499. assert ('$value == $prefs_cache[$pref_name]');
  500. sqsession_register($prefs_cache , 'prefs_cache');
  501. return;
  502. }
  503. /**
  504. * This checks if the prefs are available
  505. * @ignore
  506. */
  507. function checkForPrefs($data_dir, $username) {
  508. $db = new dbPrefs;
  509. if(isset($db->error)) {
  510. $db->failQuery();
  511. }
  512. }
  513. /**
  514. * Writes the Signature
  515. * @ignore
  516. */
  517. function setSig($data_dir, $username, $number, $value) {
  518. if ($number == "g") {
  519. $key = '___signature___';
  520. } else {
  521. $key = sprintf('___sig%s___', $number);
  522. }
  523. setPref($data_dir, $username, $key, $value);
  524. return;
  525. }
  526. /**
  527. * Gets the signature
  528. * @ignore
  529. */
  530. function getSig($data_dir, $username, $number) {
  531. if ($number == "g") {
  532. $key = '___signature___';
  533. } else {
  534. $key = sprintf('___sig%d___', $number);
  535. }
  536. return getPref($data_dir, $username, $key);
  537. }