db_prefs.php 17 KB

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