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