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