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