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