Replacing tabs with spaces, trimming white space at EOL and newline at EOF
[squirrelmail.git] / plugins / squirrelspell / sqspell_functions.php
CommitLineData
849bdf42 1<?php
15e6162e 2/**
91e0dccc 3 * sqspell_functions.php
d112ed5a 4 * ----------------------
5 * All SquirrelSpell-wide functions are in this file.
15e6162e 6 *
82d304a0 7 * Copyright (c) 1999-2004 The SquirrelMail development team
15e6162e 8 * Licensed under the GNU GPL. For full terms see the file COPYING.
9 *
10 * $Id$
d112ed5a 11 *
12 * @author Konstantin Riabitsev <icon@duke.edu> ($Author$)
13 * @version $Date$
ea5f4b8e 14 * @package plugins
15 * @subpackage squirrelspell
15e6162e 16 */
d112ed5a 17
18/**
19 * This function is the GUI wrapper for the options page. SquirrelSpell
20 * uses it for creating all Options pages.
21 *
22 * @param $title The title of the page to display
91e0dccc 23 * @param $scriptsrc This is used to link a file.js into the
d112ed5a 24 * <script src="file.js"></script> format. This
25 * allows to separate javascript from the rest of the
26 * plugin and place it into the js/ directory.
27 * @param $body The body of the message to display.
28 * @return void
29 */
30function sqspell_makePage($title, $scriptsrc, $body){
8a9f9d09 31 global $color, $SQSPELL_VERSION;
32
b587ac51 33 if (! sqgetGlobalVar('MOD', $MOD, SQ_GET) ) {
8a9f9d09 34 $MOD = 'options_main';
35 }
36
91e0dccc 37 displayPageHeader($color, 'None');
2ad4cea9 38 echo "&nbsp;<br />\n";
d112ed5a 39 /**
40 * Check if we need to link in a script.
41 */
91e0dccc 42 if($scriptsrc) {
d112ed5a 43 echo "<script type=\"text/javascript\" src=\"js/$scriptsrc\"></script>\n";
44 }
484ed7c9 45 echo html_tag( 'table', '', 'center', '', 'width="95%" border="0" cellpadding="2" cellspacing="0"' ) . "\n"
46 . html_tag( 'tr', "\n" .
47 html_tag( 'td', '<strong>' . $title .'</strong>', 'center', $color[9] )
48 ) . "\n"
49 . html_tag( 'tr', "\n" .
04fa3c41 50 html_tag( 'td', '<hr />', 'left' )
484ed7c9 51 ) . "\n"
52 . html_tag( 'tr', "\n" .
53 html_tag( 'td', $body, 'left' )
54 ) . "\n";
d112ed5a 55 /**
56 * Generate a nice "Return to Options" link, unless this is the
57 * starting page.
58 */
91e0dccc 59 if ($MOD != "options_main"){
484ed7c9 60 echo html_tag( 'tr', "\n" .
04fa3c41 61 html_tag( 'td', '<hr />', 'left' )
484ed7c9 62 ) . "\n"
63 . html_tag( 'tr', "\n" .
64 html_tag( 'td', '<a href="sqspell_options.php">'
65 . _("Back to &quot;SpellChecker Options&quot; page")
66 . '</a>',
67 'center' )
68 ) . "\n";
d112ed5a 69 }
70 /**
71 * Close the table and display the version.
72 */
484ed7c9 73 echo html_tag( 'tr', "\n" .
04fa3c41 74 html_tag( 'td', '<hr />', 'left' )
484ed7c9 75 ) . "\n"
76 . html_tag( 'tr',
77 html_tag( 'td', 'SquirrelSpell ' . $SQSPELL_VERSION, 'center', $color[9] )
f6536dcf 78 ) . "\n</table>\n";
dcc1cc82 79 echo '</body></html>';
d112ed5a 80}
81
82/**
83 * Function similar to the one above. This one is a general wrapper
84 * for the Squirrelspell pop-up window. It's called form nearly
85 * everywhere, except the check_me module, since that one is highly
86 * customized.
87 *
88 * @param $onload Used to indicate and pass the name of a js function
89 * to call in a <body onload="function()" for automatic
90 * onload script execution.
91 * @param $title Title of the page.
92 * @param $scriptsrc If defined, link this javascript source page into
93 * the document using <script src="file.js"> format.
94 * @param $body The content to include.
95 * @return void
96 */
97function sqspell_makeWindow($onload, $title, $scriptsrc, $body){
fff30237 98 global $color, $SQSPELL_VERSION;
99
100 displayHtmlHeader($title,
101 ($scriptsrc ? "\n<script type=\"text/javascript\" src=\"js/$scriptsrc\"></script>\n" : ''));
102
103 echo "<body text=\"$color[8]\" bgcolor=\"$color[4]\" link=\"$color[7]\" "
104 . "vlink=\"$color[7]\" alink=\"$color[7]\"";
d112ed5a 105 /**
106 * Provide an onload="jsfunction()" if asked to.
107 */
108 if ($onload) {
fff30237 109 echo " onload=\"$onload\"";
d112ed5a 110 }
111 /**
112 * Draw the rest of the page.
113 */
fff30237 114 echo ">\n"
484ed7c9 115 . html_tag( 'table', "\n" .
116 html_tag( 'tr', "\n" .
117 html_tag( 'td', '<strong>' . $title . '</strong>', 'center', $color[9] )
118 ) . "\n" .
119 html_tag( 'tr', "\n" .
04fa3c41 120 html_tag( 'td', '<hr />', 'left' )
484ed7c9 121 ) . "\n" .
122 html_tag( 'tr', "\n" .
123 html_tag( 'td', $body, 'left' )
124 ) . "\n" .
125 html_tag( 'tr', "\n" .
04fa3c41 126 html_tag( 'td', '<hr />', 'left' )
484ed7c9 127 ) . "\n" .
128 html_tag( 'tr', "\n" .
129 html_tag( 'td', 'SquirrelSpell ' . $SQSPELL_VERSION, 'center', $color[9] )
130 ) ,
131 '', '', 'width="100%" border="0" cellpadding="2"' )
d112ed5a 132 . "</body>\n</html>\n";
133}
134
135/**
136 * This function does the encryption and decryption of the user
91e0dccc 137 * dictionary. It is only available when PHP is compiled with
d112ed5a 138 * mcrypt support (--with-mcrypt). See doc/CRYPTO for more
139 * information.
140 *
141 * @param $mode A string with either of the two recognized values:
142 * "encrypt" or "decrypt".
143 * @param $ckey The key to use for processing (the user's password
144 * in our case.
145 * @param $input Content to decrypt or encrypt, according to $mode.
146 * @return encrypted/decrypted content, or "PANIC" if the
147 * process bails out.
148 */
149function sqspell_crypto($mode, $ckey, $input){
150 /**
151 * Double-check if we have the mcrypt_generic function. Bail out if
152 * not so.
153 */
1c2a03c3 154 if (!function_exists('mcrypt_generic')) {
d112ed5a 155 return 'PANIC';
156 }
157 /**
158 * Setup mcrypt routines.
159 */
160 $td = mcrypt_module_open(MCRYPT_Blowfish, "", MCRYPT_MODE_ECB, "");
161 $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
162 mcrypt_generic_init($td, $ckey, $iv);
163 /**
164 * See what we have to do depending on $mode.
165 * 'encrypt' -- Encrypt the content.
166 * 'decrypt' -- Decrypt the content.
167 */
168 switch ($mode){
169 case 'encrypt':
170 $crypto = mcrypt_generic($td, $input);
171 break;
172 case 'decrypt':
173 $crypto = mdecrypt_generic($td, $input);
91e0dccc 174 /**
d112ed5a 175 * See if it decrypted successfully. If so, it should contain
176 * the string "# SquirrelSpell". If not, then bail out.
177 */
178 if (!strstr($crypto, "# SquirrelSpell")){
179 $crypto='PANIC';
9804bcde 180 }
d112ed5a 181 break;
182 }
183 /**
184 * Finish up the mcrypt routines and return the processed content.
185 */
1c2a03c3 186 if (function_exists('mcrypt_generic_deinit')) {
187 // php 4.1.1+ syntax
188 mcrypt_generic_deinit ($td);
189 mcrypt_module_close ($td);
190 } else {
b765c366 191 // older deprecated function
1c2a03c3 192 mcrypt_generic_end ($td);
193 }
d112ed5a 194 return $crypto;
195}
196
197/**
198 * This function transparently upgrades the 0.2 dictionary format to the
199 * 0.3 format, since user-defined languages have been added in 0.3 and
200 * the new format keeps user dictionaries selection in the file.
201 *
202 * This function will be retired soon, as it's been a while since anyone
203 * has been using SquirrelSpell-0.2.
204 *
205 * @param $words_string Contents of the 0.2-style user dictionary.
206 * @return Contents of the 0.3-style user dictionary.
91e0dccc 207 */
d112ed5a 208function sqspell_upgradeWordsFile($words_string){
209 global $SQSPELL_APP_DEFAULT, $SQSPELL_VERSION;
210 /**
211 * Define just one dictionary for this user -- the default.
212 * If the user wants more, s/he can set them up in personal
213 * preferences. See doc/UPGRADING for more info.
214 */
91e0dccc 215 $new_words_string =
216 substr_replace($words_string,
217 "# SquirrelSpell User Dictionary $SQSPELL_VERSION\n# "
218 . "Last Revision: " . date("Y-m-d")
219 . "\n# LANG: $SQSPELL_APP_DEFAULT\n# $SQSPELL_APP_DEFAULT",
220 0, strpos($words_string, "\n")) . "# End\n";
d112ed5a 221 sqspell_writeWords($new_words_string);
222 return $new_words_string;
223}
224
225/**
91e0dccc 226 * Right now it just returns an array with the dictionaries
d112ed5a 227 * available to the user for spell-checking. It will probably
228 * do more in the future, as features are added.
229 *
230 * @param $words The contents of the user's ".words" file.
231 * @return a strings array with dictionaries available
232 * to this user, e.g. {"English", "Spanish"}, etc.
233 */
234function sqspell_getSettings($words){
235 global $SQSPELL_APP, $SQSPELL_APP_DEFAULT;
236 /**
237 * Check if there is more than one dictionary configured in the
238 * system config.
239 */
240 if (sizeof($SQSPELL_APP) > 1){
241 /**
242 * Now load the user prefs. Check if $words was empty -- a bit of
243 * a dirty fall-back. TODO: make it so this is not required.
244 */
245 if(!$words){
246 $words=sqspell_getWords();
9804bcde 247 }
d112ed5a 248 if ($words){
249 /**
250 * This user has a ".words" file.
251 * Find which dictionaries s/he wants to use and load them into
252 * the $langs array.
253 */
254 preg_match("/# LANG: (.*)/i", $words, $matches);
255 $langs=explode(", ", $matches[1]);
256 } else {
257 /**
258 * User doesn't have a personal dictionary. Grab the default
259 * system setting.
260 */
261 $langs[0]=$SQSPELL_APP_DEFAULT;
9804bcde 262 }
d112ed5a 263 } else {
264 /**
265 * There is no need to read the ".words" file as there is only one
266 * dictionary defined system-wide.
267 */
268 $langs[0]=$SQSPELL_APP_DEFAULT;
269 }
270 return $langs;
271}
272
273/**
274 * This function returns only user-defined dictionary words that correspond
275 * to the requested language.
276 *
277 * @param $words The contents of the user's ".words" file.
91e0dccc 278 * @param $lang Which language words to return, e.g. requesting
d112ed5a 279 * "English" will return ONLY the words from user's
280 * English dictionary, disregarding any others.
281 * @return The list of words corresponding to the language
282 * requested.
91e0dccc 283 */
d112ed5a 284function sqspell_getLang($words, $lang){
285 $start=strpos($words, "# $lang\n");
286 /**
287 * strpos() will return -1 if no # $lang\n string was found.
288 * Use this to return a zero-length value and indicate that no
289 * words are present in the requested dictionary.
290 */
291 if (!$start) return '';
292 /**
293 * The words list will end with a new directive, which will start
294 * with "#". Locate the next "#" and thus find out where the
295 * words end.
296 */
297 $end=strpos($words, "#", $start+1);
298 $lang_words = substr($words, $start, $end-$start);
299 return $lang_words;
300}
301
302/**
303 * This function operates the user dictionary. If the format is
304 * clear-text, then it just reads the file and returns it. However, if
305 * the file is encrypted (well, "garbled"), then it tries to decrypt
306 * it, checks whether the decryption was successful, troubleshoots if
307 * not, then returns the clear-text dictionary to the app.
91e0dccc 308 *
309 * @return the contents of the user's ".words" file, decrypted if
d112ed5a 310 * necessary.
311 */
312function sqspell_getWords(){
313 global $SQSPELL_WORDS_FILE, $SQSPELL_CRYPTO;
314 $words="";
315 if (file_exists($SQSPELL_WORDS_FILE)){
316 /**
317 * Gobble it up.
318 */
319 $fp=fopen($SQSPELL_WORDS_FILE, 'r');
320 $words=fread($fp, filesize($SQSPELL_WORDS_FILE));
321 fclose($fp);
322 }
323 /**
324 * Check if this is an encrypted file by looking for
325 * the string "# SquirrelSpell" in it (the crypto
326 * function does that).
327 */
328 if ($words && !strstr($words, "# SquirrelSpell")){
329 /**
330 * This file is encrypted or mangled. Try to decrypt it.
331 * If fails, complain loudly.
332 *
333 * $old_key would be a value submitted by one of the modules with
334 * the user's old mailbox password. I admin, this is rather dirty,
335 * but efficient. ;)
336 */
b587ac51 337 sqgetGlobalVar('key', $key, SQ_COOKIE);
338 sqgetGlobalVar('onetimepad', $onetimepad, SQ_SESSION);
339
340 sqgetGlobalVar('old_key', $old_key, SQ_POST);
8a9f9d09 341
342 if ($old_key != '') {
343 $clear_key=$old_key;
d112ed5a 344 } else {
345 /**
346 * Get user's password (the key).
347 */
348 $clear_key = OneTimePadDecrypt($key, $onetimepad);
9804bcde 349 }
d112ed5a 350 /**
351 * Invoke the decryption routines.
352 */
353 $words=sqspell_crypto("decrypt", $clear_key, $words);
354 /**
355 * See if decryption failed.
356 */
357 if ($words=="PANIC"){
358 /**
359 * AAAAAAAAAAAH!!!!! OK, ok, breathe!
360 * Let's hope the decryption failed because the user changed his
361 * password. Bring up the option to key in the old password
362 * or wipe the file and start over if everything else fails.
363 *
364 * The _("SquirrelSpell...) line has to be on one line, otherwise
365 * gettext will bork. ;(
366 */
484ed7c9 367 $msg = html_tag( 'p', "\n" .
2ad4cea9 368 '<strong>' . _("ATTENTION:") . '</strong><br />'
369 . _("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.") ,
484ed7c9 370 'left' ) . "\n"
91e0dccc 371 . '<blockquote>' . "\n"
372 . '<form method="post" onsubmit="return AYS()">' . "\n"
373 . '<input type="hidden" name="MOD" value="crypto_badkey" />' . "\n"
374 . html_tag( 'p', "\n" .
375 '<input type="checkbox" name="delete_words" value="ON" />'
376 . _("Delete my dictionary and start a new one") . '<br />'
377 . _("Decrypt my dictionary with my old password:")
378 . '<input name="old_key" size="10" />' ,
379 'left' ) . "\n"
380 . '</blockquote>' . "\n"
381 . html_tag( 'p', "\n" .
382 '<input type="submit" value="'
383 . _("Proceed") . ' &gt;&gt;" />' ,
384 'center' ) . "\n"
385 . '</form>' . "\n";
a8380e75 386 /**
387 * Add some string vars so they can be i18n'd.
388 */
389 $msg .= "<script type='text/javascript'><!--\n"
91e0dccc 390 . "var ui_choice = \"" . _("You must make a choice") ."\";\n"
391 . "var ui_candel = \"" . _("You can either delete your dictionary or type in the old password. Not both.") . "\";\n"
392 . "var ui_willdel = \"" . _("This will delete your personal dictionary file. Proceed?") . "\";\n"
393 . "//--></script>\n";
d112ed5a 394 /**
395 * See if this happened in the pop-up window or when accessing
91e0dccc 396 * the SpellChecker options page.
d112ed5a 397 * This is a dirty solution, I agree. TODO: make this prettier.
398 */
399 global $SCRIPT_NAME;
400 if (strstr($SCRIPT_NAME, "sqspell_options")){
91e0dccc 401 sqspell_makePage(_("Error Decrypting Dictionary"),
402 "decrypt_error.js", $msg);
d112ed5a 403 } else {
91e0dccc 404 sqspell_makeWindow(null, _("Error Decrypting Dictionary"),
405 "decrypt_error.js", $msg);
d112ed5a 406 }
407 exit;
408 } else {
409 /**
91e0dccc 410 * OK! Phew. Set the encryption flag to true so we can later on
d112ed5a 411 * encrypt it again before saving to HDD.
412 */
413 $SQSPELL_CRYPTO=true;
9804bcde 414 }
d112ed5a 415 } else {
416 /**
91e0dccc 417 * No encryption is/was used. Set $SQSPELL_CRYPTO to false,
d112ed5a 418 * in case we have to save the dictionary later.
419 */
420 $SQSPELL_CRYPTO=false;
421 }
422 /**
423 * Check if we need to upgrade the dictionary from version 0.2.x
424 * This is going away soon.
425 */
426 if (strstr($words, "Dictionary v0.2")){
427 $words=sqspell_upgradeWordsFile($words);
428 }
429 return $words;
430}
91e0dccc 431
d112ed5a 432/**
433 * Writes user dictionary into the $username.words file, then changes mask
434 * to 0600. If encryption is needed -- does that, too.
435 *
436 * @param $words The contents of the ".words" file to write.
437 * @return void
438 */
439function sqspell_writeWords($words){
440 global $SQSPELL_WORDS_FILE, $SQSPELL_CRYPTO;
441 /**
442 * if $words is empty, create a template entry by calling the
443 * sqspell_makeDummy() function.
444 */
445 if (!$words){
446 $words=sqspell_makeDummy();
447 }
448 if ($SQSPELL_CRYPTO){
449 /**
450 * User wants to encrypt the file. So be it.
451 * Get the user's password to use as a key.
452 */
b587ac51 453 sqgetGlobalVar('key', $key, SQ_COOKIE);
454 sqgetGlobalVar('onetimepad', $onetimepad, SQ_SESSION);
8a9f9d09 455
d112ed5a 456 $clear_key=OneTimePadDecrypt($key, $onetimepad);
457 /**
458 * Try encrypting it. If fails, scream bloody hell.
459 */
460 $save_words = sqspell_crypto("encrypt", $clear_key, $words);
461 if ($save_words == 'PANIC'){
462 /**
463 * AAAAAAAAH! I'm not handling this yet, since obviously
464 * the admin of the site forgot to compile the MCRYPT support in
465 * when upgrading an existing PHP installation.
466 * I will add a handler for this case later, when I can come up
467 * with some work-around... Right now, do nothing. Let the Admin's
468 * head hurt.. ;)))
469 */
9804bcde 470 }
d112ed5a 471 } else {
472 $save_words = $words;
473 }
474 /**
475 * Do the actual writing.
476 */
477 $fp=fopen($SQSPELL_WORDS_FILE, "w");
478 fwrite($fp, $save_words);
479 fclose($fp);
480 chmod($SQSPELL_WORDS_FILE, 0600);
481}
91e0dccc 482
d112ed5a 483function sqspell_deleteWords(){
484 /**
485 * So I open the door to my enemies,
486 * and I ask can we wipe the slate clean,
487 * but they tell me to please go...
488 * uhm... Well, this just erases the user dictionary file.
489 */
490 global $SQSPELL_WORDS_FILE;
491 if (file_exists($SQSPELL_WORDS_FILE)){
492 unlink($SQSPELL_WORDS_FILE);
493 }
494}
495/**
496 * Creates an empty user dictionary for the sake of saving prefs or
497 * whatever.
498 *
499 * @return The template to use when storing the user dictionary.
91e0dccc 500 */
d112ed5a 501function sqspell_makeDummy(){
502 global $SQSPELL_VERSION, $SQSPELL_APP_DEFAULT;
503 $words = "# SquirrelSpell User Dictionary $SQSPELL_VERSION\n"
91e0dccc 504 . "# Last Revision: " . date('Y-m-d')
505 . "\n# LANG: $SQSPELL_APP_DEFAULT\n# End\n";
d112ed5a 506 return $words;
507}
508
509/**
510 * This function checks for security attacks. A $MOD variable is
511 * provided in the QUERY_STRING and includes one of the files from the
512 * modules directory ($MOD.mod). See if someone is trying to get out
513 * of the modules directory by providing dots, unicode strings, or
514 * slashes.
515 *
516 * @param $rMOD the name of the module requested to include.
517 * @return void, since it bails out with an access error if needed.
518 */
519function sqspell_ckMOD($rMOD){
91e0dccc 520 if (strstr($rMOD, '.')
521 || strstr($rMOD, '/')
d112ed5a 522 || strstr($rMOD, '%')
91e0dccc 523 || strstr($rMOD, "\\")){
d112ed5a 524 echo _("Cute.");
525 exit;
526 }
527}
528
529/**
530 * SquirrelSpell version. Don't modify, since it identifies the format
91e0dccc 531 * of the user dictionary files and messing with this can do ugly
d112ed5a 532 * stuff. :)
533 */
d88c744e 534$SQSPELL_VERSION="v0.3.8";
04fa3c41 535?>