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