sqspell_functions.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. <?php
  2. /**
  3. * sqspell_functions.php
  4. *
  5. * All SquirrelSpell-wide functions are in this file.
  6. *
  7. * @author Konstantin Riabitsev <icon at duke.edu>
  8. * @copyright &copy; 1999-2007 The SquirrelMail Project Team
  9. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10. * @version $Id$
  11. * @package plugins
  12. * @subpackage squirrelspell
  13. */
  14. /** globalize configuration vars **/
  15. global $SQSPELL_APP, $SQSPELL_APP_DEFAULT, $SQSPELL_WORDS_FILE, $SQSPELL_CRYPTO;
  16. /**
  17. * load plugin configuration
  18. * @todo allow storing configuration file in config/ directory
  19. */
  20. include_once(SM_PATH . 'plugins/squirrelspell/sqspell_config.php');
  21. /**
  22. * Workaround for including function squirrelspell_version() in SM 1.5 CVS,
  23. * where plugins' setup.php is not included by default.
  24. */
  25. include_once(SM_PATH . 'plugins/squirrelspell/setup.php');
  26. /** Hooked functions **/
  27. /**
  28. * Register option page block (internal function)
  29. * @since 1.5.1 (sqspell 0.5)
  30. * @return void
  31. */
  32. function squirrelspell_optpage_block_function() {
  33. global $optpage_blocks;
  34. /**
  35. * Dependency on JavaScript is checked by SquirrelMail scripts
  36. * Register Squirrelspell with the $optpage_blocks array.
  37. */
  38. $optpage_blocks[] =
  39. array(
  40. 'name' => _("SpellChecker Options"),
  41. 'url' => '../plugins/squirrelspell/sqspell_options.php',
  42. 'desc' => _("Here you may set up how your personal dictionary is stored, edit it, or choose which languages should be available to you when spell-checking."),
  43. 'js' => TRUE);
  44. }
  45. /**
  46. * This function adds a "Check Spelling" link to the "Compose" row
  47. * during message composition (internal function).
  48. * @since 1.5.1 (sqspell 0.5)
  49. * @return void
  50. */
  51. function squirrelspell_setup_function() {
  52. /**
  53. * Check if this browser is capable of displaying SquirrelSpell
  54. * correctly.
  55. */
  56. if (checkForJavascript()) {
  57. /**
  58. * Some people may choose to disable javascript even though their
  59. * browser is capable of using it. So these freaks don't complain,
  60. * use document.write() so the "Check Spelling" button is not
  61. * displayed if js is off in the browser.
  62. */
  63. $output = "<script type=\"text/javascript\">\n".
  64. "<!--\n".
  65. 'document.write("<input type=\"button\" value=\"'.
  66. _("Check Spelling").
  67. '\" name=\"check_spelling\" onclick=\"window.open(\'../plugins/squirrelspell/sqspell_'.
  68. 'interface.php\', \'sqspell\', \'status=yes,width=550,height=370,'.
  69. 'resizable=yes\')\" />");' . "\n".
  70. "//-->\n".
  71. "</script>\n";
  72. return array('compose_button_row' => $output);
  73. }
  74. }
  75. /**
  76. * Upgrade dictionaries (internal function)
  77. *
  78. * Transparently upgrades user's dictionaries when message listing is loaded
  79. * @since 1.5.1 (sqspell 0.5)
  80. */
  81. function squirrelspell_upgrade_function() {
  82. global $data_dir, $username;
  83. if (! sqspell_check_version(0,5)) {
  84. $langs=sqspell_getSettings_old(null);
  85. $words=sqspell_getWords_old();
  86. sqspell_saveSettings($langs);
  87. foreach ($langs as $lang) {
  88. $lang_words=sqspell_getLang_old($words,$lang);
  89. $aLang_words=explode("\n",$lang_words);
  90. $new_words=array();
  91. foreach($aLang_words as $word) {
  92. if (! preg_match("/^#/",$word) && trim($word)!='') {
  93. $new_words[]=$word;
  94. }
  95. }
  96. sqspell_writeWords($new_words,$lang);
  97. }
  98. // bump up version number
  99. setPref($data_dir,$username,'sqspell_version','0.5');
  100. }
  101. }
  102. /** Internal functions **/
  103. /**
  104. * This function is the GUI wrapper for the options page. SquirrelSpell
  105. * uses it for creating all Options pages.
  106. *
  107. * @param string $title The title of the page to display
  108. * @param string $scriptsrc This is used to link a file.js into the
  109. * <script src="file.js"></script> format. This
  110. * allows to separate javascript from the rest of the
  111. * plugin and place it into the js/ directory.
  112. * @param string $body The body of the message to display.
  113. * @return void
  114. */
  115. function sqspell_makePage($title, $scriptsrc, $body){
  116. global $color, $SQSPELL_VERSION;
  117. if (! sqgetGlobalVar('MOD', $MOD, SQ_GET) ) {
  118. $MOD = 'options_main';
  119. }
  120. displayPageHeader($color, 'None');
  121. echo "&nbsp;<br />\n";
  122. /**
  123. * Check if we need to link in a script.
  124. */
  125. if($scriptsrc) {
  126. echo "<script type=\"text/javascript\" src=\"js/$scriptsrc\"></script>\n";
  127. }
  128. echo html_tag( 'table', '', 'center', '', 'width="95%" border="0" cellpadding="2" cellspacing="0"' ) . "\n"
  129. . html_tag( 'tr', "\n" .
  130. html_tag( 'td', '<strong>' . $title .'</strong>', 'center', $color[9] )
  131. ) . "\n"
  132. . html_tag( 'tr', "\n" .
  133. html_tag( 'td', '<hr />', 'left' )
  134. ) . "\n"
  135. . html_tag( 'tr', "\n" .
  136. html_tag( 'td', $body, 'left' )
  137. ) . "\n";
  138. /**
  139. * Generate a nice "Return to Options" link, unless this is the
  140. * starting page.
  141. */
  142. if ($MOD != "options_main"){
  143. echo html_tag( 'tr', "\n" .
  144. html_tag( 'td', '<hr />', 'left' )
  145. ) . "\n"
  146. . html_tag( 'tr', "\n" .
  147. html_tag( 'td', '<a href="sqspell_options.php">'
  148. . _("Back to &quot;SpellChecker Options&quot; page")
  149. . '</a>',
  150. 'center' )
  151. ) . "\n";
  152. }
  153. /**
  154. * Close the table and display the version.
  155. */
  156. echo html_tag( 'tr', "\n" .
  157. html_tag( 'td', '<hr />', 'left' )
  158. ) . "\n"
  159. . html_tag( 'tr',
  160. html_tag( 'td', 'SquirrelSpell ' . squirrelspell_version(), 'center', $color[9] )
  161. ) . "\n</table>\n";
  162. echo '</body></html>';
  163. }
  164. /**
  165. * Function similar to the one above. This one is a general wrapper
  166. * for the Squirrelspell pop-up window. It's called form nearly
  167. * everywhere, except the check_me module, since that one is highly
  168. * customized.
  169. *
  170. * @param string $onload Used to indicate and pass the name of a js function
  171. * to call in a <body onload="function()" for automatic
  172. * onload script execution.
  173. * @param string $title Title of the page.
  174. * @param string $scriptsrc If defined, link this javascript source page into
  175. * the document using <script src="file.js"> format.
  176. * @param string $body The content to include.
  177. * @return void
  178. */
  179. function sqspell_makeWindow($onload, $title, $scriptsrc, $body){
  180. global $color, $SQSPELL_VERSION;
  181. displayHtmlHeader($title,
  182. ($scriptsrc ? "\n<script type=\"text/javascript\" src=\"js/$scriptsrc\"></script>\n" : ''));
  183. echo "<body text=\"$color[8]\" bgcolor=\"$color[4]\" link=\"$color[7]\" "
  184. . "vlink=\"$color[7]\" alink=\"$color[7]\"";
  185. /**
  186. * Provide an onload="jsfunction()" if asked to.
  187. */
  188. if ($onload) {
  189. echo " onload=\"$onload\"";
  190. }
  191. /**
  192. * Draw the rest of the page.
  193. */
  194. echo ">\n"
  195. . html_tag( 'table', "\n" .
  196. html_tag( 'tr', "\n" .
  197. html_tag( 'td', '<strong>' . $title . '</strong>', 'center', $color[9] )
  198. ) . "\n" .
  199. html_tag( 'tr', "\n" .
  200. html_tag( 'td', '<hr />', 'left' )
  201. ) . "\n" .
  202. html_tag( 'tr', "\n" .
  203. html_tag( 'td', $body, 'left' )
  204. ) . "\n" .
  205. html_tag( 'tr', "\n" .
  206. html_tag( 'td', '<hr />', 'left' )
  207. ) . "\n" .
  208. html_tag( 'tr', "\n" .
  209. html_tag( 'td', 'SquirrelSpell ' . squirrelspell_version(), 'center', $color[9] )
  210. ) ,
  211. '', '', 'width="100%" border="0" cellpadding="2"' );
  212. global $oTemplate;
  213. $oTemplate->display('footer.tpl');
  214. }
  215. /**
  216. * Encryption function used by plugin (old format)
  217. *
  218. * This function does the encryption and decryption of the user
  219. * dictionary. It is only available when PHP is compiled with
  220. * mcrypt support (--with-mcrypt). See doc/CRYPTO for more
  221. * information.
  222. *
  223. * @param $mode A string with either of the two recognized values:
  224. * "encrypt" or "decrypt".
  225. * @param $ckey The key to use for processing (the user's password
  226. * in our case.
  227. * @param $input Content to decrypt or encrypt, according to $mode.
  228. * @return encrypted/decrypted content, or "PANIC" if the
  229. * process bails out.
  230. * @since 1.5.1 (sqspell 0.5)
  231. * @deprecated
  232. */
  233. function sqspell_crypto_old($mode, $ckey, $input){
  234. /**
  235. * Double-check if we have the mcrypt_generic function. Bail out if
  236. * not so.
  237. */
  238. if (!function_exists('mcrypt_generic')) {
  239. return 'PANIC';
  240. }
  241. /**
  242. * Setup mcrypt routines.
  243. */
  244. $td = mcrypt_module_open(MCRYPT_Blowfish, "", MCRYPT_MODE_ECB, "");
  245. $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
  246. mcrypt_generic_init($td, $ckey, $iv);
  247. /**
  248. * See what we have to do depending on $mode.
  249. * 'encrypt' -- Encrypt the content.
  250. * 'decrypt' -- Decrypt the content.
  251. */
  252. switch ($mode){
  253. case 'encrypt':
  254. $crypto = mcrypt_generic($td, $input);
  255. break;
  256. case 'decrypt':
  257. $crypto = mdecrypt_generic($td, $input);
  258. /**
  259. * See if it decrypted successfully. If so, it should contain
  260. * the string "# SquirrelSpell". If not, then bail out.
  261. */
  262. if (!strstr($crypto, "# SquirrelSpell")){
  263. $crypto='PANIC';
  264. }
  265. break;
  266. }
  267. /**
  268. * Finish up the mcrypt routines and return the processed content.
  269. */
  270. if (function_exists('mcrypt_generic_deinit')) {
  271. // php 4.1.1+ syntax
  272. mcrypt_generic_deinit ($td);
  273. mcrypt_module_close ($td);
  274. } else {
  275. // older deprecated function
  276. mcrypt_generic_end ($td);
  277. }
  278. return $crypto;
  279. }
  280. /**
  281. * Encryption function used by plugin
  282. *
  283. * This function does the encryption and decryption of the user
  284. * dictionary. It is only available when PHP is compiled with
  285. * mcrypt support (--with-mcrypt). See doc/CRYPTO for more
  286. * information.
  287. *
  288. * @param $mode A string with either of the two recognized values:
  289. * "encrypt" or "decrypt".
  290. * @param $ckey The key to use for processing (the user's password
  291. * in our case.
  292. * @param $input Content to decrypt or encrypt, according to $mode.
  293. * @return encrypted/decrypted content, or "PANIC" if the
  294. * process bails out.
  295. */
  296. function sqspell_crypto($mode, $ckey, $input){
  297. /**
  298. * Double-check if we have the mcrypt_generic function. Bail out if
  299. * not so.
  300. */
  301. if (!function_exists('mcrypt_generic')) {
  302. return 'PANIC';
  303. }
  304. /**
  305. * Setup mcrypt routines.
  306. */
  307. $td = mcrypt_module_open(MCRYPT_Blowfish, "", MCRYPT_MODE_ECB, "");
  308. $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
  309. mcrypt_generic_init($td, $ckey, $iv);
  310. /**
  311. * See what we have to do depending on $mode.
  312. * 'encrypt' -- Encrypt the content.
  313. * 'decrypt' -- Decrypt the content.
  314. */
  315. switch ($mode){
  316. case 'encrypt':
  317. $crypto = mcrypt_generic($td, '{sqspell}'.$input);
  318. break;
  319. case 'decrypt':
  320. $crypto = mdecrypt_generic($td, $input);
  321. if (preg_match("/^\{sqspell\}(.*)/",$crypto,$match)){
  322. $crypto = trim($match[1]);
  323. } else {
  324. $crypto='PANIC';
  325. }
  326. break;
  327. }
  328. /**
  329. * Finish up the mcrypt routines and return the processed content.
  330. */
  331. if (function_exists('mcrypt_generic_deinit')) {
  332. // php 4.1.1+ syntax
  333. mcrypt_generic_deinit ($td);
  334. mcrypt_module_close ($td);
  335. } else {
  336. // older deprecated function
  337. mcrypt_generic_end ($td);
  338. }
  339. return $crypto;
  340. }
  341. /**
  342. * This function transparently upgrades the 0.2 dictionary format to the
  343. * 0.3 format, since user-defined languages have been added in 0.3 and
  344. * the new format keeps user dictionaries selection in the file.
  345. *
  346. * This function will be retired soon, as it's been a while since anyone
  347. * has been using SquirrelSpell-0.2.
  348. *
  349. * @param $words_string Contents of the 0.2-style user dictionary.
  350. * @return Contents of the 0.3-style user dictionary.
  351. * @deprecated
  352. */
  353. function sqspell_upgradeWordsFile($words_string){
  354. global $SQSPELL_APP_DEFAULT, $SQSPELL_VERSION;
  355. /**
  356. * Define just one dictionary for this user -- the default.
  357. * If the user wants more, s/he can set them up in personal
  358. * preferences. See doc/UPGRADING for more info.
  359. */
  360. $new_words_string =
  361. substr_replace($words_string,
  362. "# SquirrelSpell User Dictionary $SQSPELL_VERSION\n# "
  363. . "Last Revision: " . date("Y-m-d")
  364. . "\n# LANG: $SQSPELL_APP_DEFAULT\n# $SQSPELL_APP_DEFAULT",
  365. 0, strpos($words_string, "\n")) . "# End\n";
  366. sqspell_writeWords($new_words_string);
  367. return $new_words_string;
  368. }
  369. /**
  370. * gets list of available dictionaries from user's prefs.
  371. * Function was modified in 1.5.1 (sqspell 0.5).
  372. * Older function is suffixed with '_old'
  373. * @return array list of dictionaries used by end user.
  374. */
  375. function sqspell_getSettings(){
  376. global $data_dir, $username, $SQSPELL_APP_DEFAULT, $SQSPELL_APP;
  377. $ret=array();
  378. $sLangs=getPref($data_dir,$username,'sqspell_langs','');
  379. if ($sLangs=='') {
  380. $ret[0]=$SQSPELL_APP_DEFAULT;
  381. } else {
  382. $aLangs = explode(',',$sLangs);
  383. foreach ($aLangs as $lang) {
  384. if (array_key_exists($lang,$SQSPELL_APP)) {
  385. $ret[]=$lang;
  386. }
  387. }
  388. }
  389. return $ret;
  390. }
  391. /**
  392. * Saves user's language preferences
  393. * @param array $langs languages array (first key is default language)
  394. * @since 1.5.1 (sqspell 0.5)
  395. */
  396. function sqspell_saveSettings($langs) {
  397. global $data_dir, $username;
  398. setPref($data_dir,$username,'sqspell_langs',implode(',',$langs));
  399. }
  400. /**
  401. * Get list of enabled languages.
  402. *
  403. * Right now it just returns an array with the dictionaries
  404. * available to the user for spell-checking. It will probably
  405. * do more in the future, as features are added.
  406. *
  407. * @param string $words The contents of the user's ".words" file.
  408. * @return array a strings array with dictionaries available
  409. * to this user, e.g. {"English", "Spanish"}, etc.
  410. * @since 1.5.1 (sqspell 0.5)
  411. * @deprecated
  412. */
  413. function sqspell_getSettings_old($words){
  414. global $SQSPELL_APP, $SQSPELL_APP_DEFAULT;
  415. /**
  416. * Check if there is more than one dictionary configured in the
  417. * system config.
  418. */
  419. if (sizeof($SQSPELL_APP) > 1){
  420. /**
  421. * Now load the user prefs. Check if $words was empty -- a bit of
  422. * a dirty fall-back. TODO: make it so this is not required.
  423. */
  424. if(!$words){
  425. $words=sqspell_getWords_old();
  426. }
  427. if ($words){
  428. /**
  429. * This user has a ".words" file.
  430. * Find which dictionaries s/he wants to use and load them into
  431. * the $langs array.
  432. */
  433. preg_match("/# LANG: (.*)/i", $words, $matches);
  434. $langs=explode(", ", $matches[1]);
  435. } else {
  436. /**
  437. * User doesn't have a personal dictionary. Grab the default
  438. * system setting.
  439. */
  440. $langs[0]=$SQSPELL_APP_DEFAULT;
  441. }
  442. } else {
  443. /**
  444. * There is no need to read the ".words" file as there is only one
  445. * dictionary defined system-wide.
  446. */
  447. $langs[0]=$SQSPELL_APP_DEFAULT;
  448. }
  449. return $langs;
  450. }
  451. /**
  452. * Get user dictionary for selected language
  453. * Function was modified in 1.5.1 (sqspell 0.5).
  454. * Older function is suffixed with '_old'
  455. * @param string $lang language
  456. * @param array words stored in selected language dictionary
  457. */
  458. function sqspell_getLang($lang) {
  459. global $data_dir, $username,$SQSPELL_CRYPTO;
  460. $sWords=getPref($data_dir,$username,'sqspell_dict_' . $lang,'');
  461. if (preg_match("/^\{crypt\}(.*)/i",$sWords,$match)) {
  462. /**
  463. * Dictionary is encrypted or mangled. Try to decrypt it.
  464. * If fails, complain loudly.
  465. *
  466. * $old_key would be a value submitted by one of the modules with
  467. * the user's old mailbox password. I admin, this is rather dirty,
  468. * but efficient. ;)
  469. */
  470. if (sqgetGlobalVar('old_key', $old_key, SQ_POST)) {
  471. $clear_key=$old_key;
  472. } else {
  473. sqgetGlobalVar('key', $key, SQ_COOKIE);
  474. sqgetGlobalVar('onetimepad', $onetimepad, SQ_SESSION);
  475. /**
  476. * Get user's password (the key).
  477. */
  478. $clear_key = OneTimePadDecrypt($key, $onetimepad);
  479. }
  480. /**
  481. * Invoke the decryption routines.
  482. */
  483. $sWords=sqspell_crypto("decrypt", $clear_key, $match[1]);
  484. /**
  485. * See if decryption failed.
  486. */
  487. if ($sWords=="PANIC"){
  488. sqspell_handle_crypt_panic($lang);
  489. // script execution stops here
  490. } else {
  491. /**
  492. * OK! Phew. Set the encryption flag to true so we can later on
  493. * encrypt it again before saving to HDD.
  494. */
  495. $SQSPELL_CRYPTO=true;
  496. }
  497. } else {
  498. /**
  499. * No encryption is/was used. Set $SQSPELL_CRYPTO to false,
  500. * in case we have to save the dictionary later.
  501. */
  502. $SQSPELL_CRYPTO=false;
  503. }
  504. // rebuild word list and remove empty entries
  505. $aWords=array();
  506. foreach (explode(',',$sWords) as $word) {
  507. if (trim($word) !='') {
  508. $aWords[]=trim($word);
  509. }
  510. }
  511. return $aWords;
  512. }
  513. /**
  514. * Get user's dictionary (old format)
  515. *
  516. * This function returns only user-defined dictionary words that correspond
  517. * to the requested language.
  518. *
  519. * @param $words The contents of the user's ".words" file.
  520. * @param $lang Which language words to return, e.g. requesting
  521. * "English" will return ONLY the words from user's
  522. * English dictionary, disregarding any others.
  523. * @return The list of words corresponding to the language
  524. * requested.
  525. * @since 1.5.1 (sqspell 0.5)
  526. * @deprecated
  527. */
  528. function sqspell_getLang_old($words, $lang){
  529. $start=strpos($words, "# $lang\n");
  530. /**
  531. * strpos() will return -1 if no # $lang\n string was found.
  532. * Use this to return a zero-length value and indicate that no
  533. * words are present in the requested dictionary.
  534. */
  535. if (!$start) return '';
  536. /**
  537. * The words list will end with a new directive, which will start
  538. * with "#". Locate the next "#" and thus find out where the
  539. * words end.
  540. */
  541. $end=strpos($words, "#", $start+1);
  542. $lang_words = substr($words, $start, $end-$start);
  543. return $lang_words;
  544. }
  545. /**
  546. * Saves user's dictionary (old format)
  547. *
  548. * This function operates the user dictionary. If the format is
  549. * clear-text, then it just reads the file and returns it. However, if
  550. * the file is encrypted (well, "garbled"), then it tries to decrypt
  551. * it, checks whether the decryption was successful, troubleshoots if
  552. * not, then returns the clear-text dictionary to the app.
  553. *
  554. * @return the contents of the user's ".words" file, decrypted if
  555. * necessary.
  556. * @since 1.5.1 (sqspell 0.5)
  557. * @deprecated
  558. */
  559. function sqspell_getWords_old(){
  560. global $SQSPELL_WORDS_FILE, $SQSPELL_CRYPTO;
  561. $words="";
  562. if (file_exists($SQSPELL_WORDS_FILE)){
  563. /**
  564. * Gobble it up.
  565. */
  566. $fp=fopen($SQSPELL_WORDS_FILE, 'r');
  567. $words=fread($fp, filesize($SQSPELL_WORDS_FILE));
  568. fclose($fp);
  569. }
  570. /**
  571. * Check if this is an encrypted file by looking for
  572. * the string "# SquirrelSpell" in it (the crypto
  573. * function does that).
  574. */
  575. if ($words && !strstr($words, "# SquirrelSpell")){
  576. /**
  577. * This file is encrypted or mangled. Try to decrypt it.
  578. * If fails, complain loudly.
  579. *
  580. * $old_key would be a value submitted by one of the modules with
  581. * the user's old mailbox password. I admin, this is rather dirty,
  582. * but efficient. ;)
  583. */
  584. sqgetGlobalVar('key', $key, SQ_COOKIE);
  585. sqgetGlobalVar('onetimepad', $onetimepad, SQ_SESSION);
  586. sqgetGlobalVar('old_key', $old_key, SQ_POST);
  587. if ($old_key != '') {
  588. $clear_key=$old_key;
  589. } else {
  590. /**
  591. * Get user's password (the key).
  592. */
  593. $clear_key = OneTimePadDecrypt($key, $onetimepad);
  594. }
  595. /**
  596. * Invoke the decryption routines.
  597. */
  598. $words=sqspell_crypto_old("decrypt", $clear_key, $words);
  599. /**
  600. * See if decryption failed.
  601. */
  602. if ($words=="PANIC"){
  603. sqspell_handle_crypt_panic();
  604. // script execution stops here.
  605. } else {
  606. /**
  607. * OK! Phew. Set the encryption flag to true so we can later on
  608. * encrypt it again before saving to HDD.
  609. */
  610. $SQSPELL_CRYPTO=true;
  611. }
  612. } else {
  613. /**
  614. * No encryption is/was used. Set $SQSPELL_CRYPTO to false,
  615. * in case we have to save the dictionary later.
  616. */
  617. $SQSPELL_CRYPTO=false;
  618. }
  619. /**
  620. * Check if we need to upgrade the dictionary from version 0.2.x
  621. * This is going away soon.
  622. */
  623. if (strstr($words, "Dictionary v0.2")){
  624. $words=sqspell_upgradeWordsFile($words);
  625. }
  626. return $words;
  627. }
  628. /**
  629. * Saves user's dictionary
  630. * Function was replaced in 1.5.1 (sqspell 0.5).
  631. * Older function is suffixed with '_old'
  632. * @param array $words words that should be stored in dictionary
  633. * @param string $lang language
  634. */
  635. function sqspell_writeWords($words,$lang){
  636. global $SQSPELL_CRYPTO,$username,$data_dir;
  637. $sWords = implode(',',$words);
  638. if ($SQSPELL_CRYPTO){
  639. /**
  640. * User wants to encrypt the file. So be it.
  641. * Get the user's password to use as a key.
  642. */
  643. sqgetGlobalVar('key', $key, SQ_COOKIE);
  644. sqgetGlobalVar('onetimepad', $onetimepad, SQ_SESSION);
  645. $clear_key=OneTimePadDecrypt($key, $onetimepad);
  646. /**
  647. * Try encrypting it. If fails, scream bloody hell.
  648. */
  649. $save_words = sqspell_crypto("encrypt", $clear_key, $sWords);
  650. if ($save_words == 'PANIC'){
  651. // FIXME: handle errors here
  652. }
  653. $save_words='{crypt}'.$save_words;
  654. } else {
  655. $save_words=$sWords;
  656. }
  657. setPref($data_dir,$username,'sqspell_dict_'.$lang,$save_words);
  658. }
  659. /**
  660. * Writes user dictionary into the $username.words file, then changes mask
  661. * to 0600. If encryption is needed -- does that, too.
  662. *
  663. * @param $words The contents of the ".words" file to write.
  664. * @return void
  665. * @since 1.5.1 (sqspell 0.5)
  666. * @deprecated
  667. */
  668. function sqspell_writeWords_old($words){
  669. global $SQSPELL_WORDS_FILE, $SQSPELL_CRYPTO;
  670. /**
  671. * if $words is empty, create a template entry by calling the
  672. * sqspell_makeDummy() function.
  673. */
  674. if (!$words){
  675. $words=sqspell_makeDummy();
  676. }
  677. if ($SQSPELL_CRYPTO){
  678. /**
  679. * User wants to encrypt the file. So be it.
  680. * Get the user's password to use as a key.
  681. */
  682. sqgetGlobalVar('key', $key, SQ_COOKIE);
  683. sqgetGlobalVar('onetimepad', $onetimepad, SQ_SESSION);
  684. $clear_key=OneTimePadDecrypt($key, $onetimepad);
  685. /**
  686. * Try encrypting it. If fails, scream bloody hell.
  687. */
  688. $save_words = sqspell_crypto("encrypt", $clear_key, $words);
  689. if ($save_words == 'PANIC'){
  690. /**
  691. * AAAAAAAAH! I'm not handling this yet, since obviously
  692. * the admin of the site forgot to compile the MCRYPT support in
  693. * when upgrading an existing PHP installation.
  694. * I will add a handler for this case later, when I can come up
  695. * with some work-around... Right now, do nothing. Let the Admin's
  696. * head hurt.. ;)))
  697. */
  698. /** save some hairs on admin's head and store error message in logs */
  699. error_log('SquirrelSpell: php does not have mcrypt support');
  700. }
  701. } else {
  702. $save_words = $words;
  703. }
  704. /**
  705. * Do the actual writing.
  706. */
  707. $fp=fopen($SQSPELL_WORDS_FILE, "w");
  708. fwrite($fp, $save_words);
  709. fclose($fp);
  710. chmod($SQSPELL_WORDS_FILE, 0600);
  711. }
  712. /**
  713. * Deletes user's dictionary
  714. * Function was modified in 1.5.1 (sqspell 0.5). Older function is suffixed
  715. * with '_old'
  716. * @param string $lang dictionary
  717. */
  718. function sqspell_deleteWords($lang) {
  719. global $data_dir, $username;
  720. removePref($data_dir,$username,'sqspell_dict_'.$lang);
  721. }
  722. /**
  723. * Deletes user's dictionary when it is corrupted.
  724. * @since 1.5.1 (sqspell 0.5)
  725. * @deprecated
  726. */
  727. function sqspell_deleteWords_old(){
  728. /**
  729. * So I open the door to my enemies,
  730. * and I ask can we wipe the slate clean,
  731. * but they tell me to please go...
  732. * uhm... Well, this just erases the user dictionary file.
  733. */
  734. global $SQSPELL_WORDS_FILE;
  735. if (file_exists($SQSPELL_WORDS_FILE)){
  736. unlink($SQSPELL_WORDS_FILE);
  737. }
  738. }
  739. /**
  740. * Creates an empty user dictionary for the sake of saving prefs or
  741. * whatever.
  742. *
  743. * @return The template to use when storing the user dictionary.
  744. * @deprecated
  745. */
  746. function sqspell_makeDummy(){
  747. global $SQSPELL_VERSION, $SQSPELL_APP_DEFAULT;
  748. $words = "# SquirrelSpell User Dictionary $SQSPELL_VERSION\n"
  749. . "# Last Revision: " . date('Y-m-d')
  750. . "\n# LANG: $SQSPELL_APP_DEFAULT\n# End\n";
  751. return $words;
  752. }
  753. /**
  754. * This function checks for security attacks. A $MOD variable is
  755. * provided in the QUERY_STRING and includes one of the files from the
  756. * modules directory ($MOD.mod). See if someone is trying to get out
  757. * of the modules directory by providing dots, unicode strings, or
  758. * slashes.
  759. *
  760. * @param string $rMOD the name of the module requested to include.
  761. * @return void, since it bails out with an access error if needed.
  762. */
  763. function sqspell_ckMOD($rMOD){
  764. if (strstr($rMOD, '.')
  765. || strstr($rMOD, '/')
  766. || strstr($rMOD, '%')
  767. || strstr($rMOD, "\\")){
  768. echo _("Cute.");
  769. exit;
  770. }
  771. }
  772. /**
  773. * Used to check internal version of SquirrelSpell dictionary
  774. * @param integer $major main version number
  775. * @param integer $minor second version number
  776. * @return boolean true if stored dictionary version is $major.$minor or newer
  777. * @since 1.5.1 (sqspell 0.5)
  778. */
  779. function sqspell_check_version($major,$minor) {
  780. global $data_dir, $username;
  781. // 0.4 version is internal version number that is used to indicate upgrade from
  782. // separate files to generic SquirrelMail prefs storage.
  783. $sqspell_version=getPref($data_dir,$username,'sqspell_version','0.4');
  784. $aVersion=explode('.',$sqspell_version);
  785. if ($aVersion[0] < $major ||
  786. ( $aVersion[0] == $major && $aVersion[1] < $minor)) {
  787. return false;
  788. }
  789. return true;
  790. }
  791. /**
  792. * Displays form that allows to enter different password for dictionary decryption.
  793. * If language is not set, function provides form to handle older dictionary files.
  794. * @param string $lang language
  795. * @since 1.5.1 (sqspell 0.5)
  796. */
  797. function sqspell_handle_crypt_panic($lang=false) {
  798. if (! sqgetGlobalVar('SCRIPT_NAME',$SCRIPT_NAME,SQ_SERVER))
  799. $SCRIPT_NAME='';
  800. /**
  801. * AAAAAAAAAAAH!!!!! OK, ok, breathe!
  802. * Let's hope the decryption failed because the user changed his
  803. * password. Bring up the option to key in the old password
  804. * or wipe the file and start over if everything else fails.
  805. *
  806. * The _("SquirrelSpell...) line has to be on one line, otherwise
  807. * gettext will bork. ;(
  808. */
  809. $msg = html_tag( 'p', "\n" .
  810. '<strong>' . _("ATTENTION:") . '</strong><br />'
  811. . _("SquirrelSpell was unable to decrypt your personal dictionary. This is most likely due to the fact that you have changed your mailbox password. In order to proceed, you will have to supply your old password so that SquirrelSpell can decrypt your personal dictionary. It will be re-encrypted with your new password after this. If you haven't encrypted your dictionary, then it got mangled and is no longer valid. You will have to delete it and start anew. This is also true if you don't remember your old password -- without it, the encrypted data is no longer accessible.") ,
  812. 'left' ) . "\n"
  813. . (($lang) ? html_tag('p',sprintf(_("Your %s dictionary is encrypted with password that differs from your current password."),
  814. htmlspecialchars($lang)),'left') : '')
  815. . '<blockquote>' . "\n"
  816. . '<form method="post" onsubmit="return AYS()">' . "\n"
  817. . '<input type="hidden" name="MOD" value="crypto_badkey" />' . "\n"
  818. . (($lang) ?
  819. '<input type="hidden" name="dict_lang" value="'.htmlspecialchars($lang).'" />' :
  820. '<input type="hidden" name="old_setup" value="yes" />')
  821. . html_tag( 'p', "\n" .
  822. '<input type="checkbox" name="delete_words" value="ON" />'
  823. . _("Delete my dictionary and start a new one") . '<br />'
  824. . _("Decrypt my dictionary with my old password:")
  825. . '<input name="old_key" size="10" />' ,
  826. 'left' ) . "\n"
  827. . '</blockquote>' . "\n"
  828. . html_tag( 'p', "\n"
  829. . '<input type="submit" value="'
  830. . _("Proceed") . ' &gt;&gt;" />' ,
  831. 'center' ) . "\n"
  832. . '</form>' . "\n";
  833. /**
  834. * Add some string vars so they can be i18n'd.
  835. */
  836. $msg .= "<script type=\"text/javascript\"><!--\n"
  837. . "var ui_choice = \"" . _("You must make a choice") ."\";\n"
  838. . "var ui_candel = \"" . _("You can either delete your dictionary or type in the old password. Not both.") . "\";\n"
  839. . "var ui_willdel = \"" . _("This will delete your personal dictionary file. Proceed?") . "\";\n"
  840. . "//--></script>\n";
  841. /**
  842. * See if this happened in the pop-up window or when accessing
  843. * the SpellChecker options page.
  844. * This is a dirty solution, I agree.
  845. * TODO: make this prettier.
  846. */
  847. if (strstr($SCRIPT_NAME, "sqspell_options")){
  848. sqspell_makePage(_("Error Decrypting Dictionary"),
  849. "decrypt_error.js", $msg);
  850. } else {
  851. sqspell_makeWindow(null, _("Error Decrypting Dictionary"),
  852. "decrypt_error.js", $msg);
  853. }
  854. exit;
  855. }
  856. /**
  857. * SquirrelSpell version. Don't modify, since it identifies the format
  858. * of the user dictionary files and messing with this can do ugly
  859. * stuff. :)
  860. * @global string $SQSPELL_VERSION
  861. * @deprecated
  862. */
  863. $SQSPELL_VERSION="v0.3.8";