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