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