quicksave/0000755000000000000000000000000011332004022011532 5ustar rootrootquicksave/functions.php0000644000000000000000000013134411331535210014270 0ustar rootroot * Copyright (c) 2003-2010 Paul Lesniewski * Licensed under the GNU GPL. For full terms see the file COPYING. * * @package plugins * @subpackage quicksave * */ /** * Present user quicksave preferences on display/compose * options page for SquirrelMail 1.5.x * */ function quicksave_options_15_do() { // SquirrelMail 1.4? bail. // if (!check_sm_version(1, 5, 2)) return; quicksave_options(); } /** * Present user quicksave preferences on display/compose * options page for SquirrelMail 1.4.x * */ function quicksave_options_14_do() { // SquirrelMail 1.5? bail. // if (check_sm_version(1, 5, 2)) return; quicksave_options(); } /** * Present user quicksave preferences on display/compose * options page * */ function quicksave_options() { global $username, $data_dir, $default_save_frequency, $default_cookie_encryption, $default_save_frequency_units, $user_can_override_save_frequency, $user_can_override_save_frequency_units, $user_can_override_encryption; include_once(SM_PATH . 'plugins/quicksave/common_functions.php'); quicksave_init(); $frequency = $default_save_frequency; $units = $default_save_frequency_units; $encryption = $default_cookie_encryption; if ($user_can_override_save_frequency) { $frequency = getPref($data_dir, $username, 'quicksave_frequency', $frequency); if ($user_can_override_save_frequency_units) $units = getPref($data_dir, $username, 'quicksave_units', $units); } if ($user_can_override_encryption) $encryption = getPref($data_dir, $username, 'quicksave_encryption', $encryption); sq_change_text_domain('quicksave'); global $optpage_data; if ($user_can_override_save_frequency) { $optpage_data['vals']['quicksave'][] = array( 'name' => 'quicksave_frequency', 'caption' => _("Message Save Frequency"), 'trailing_text' => _("(set to zero to turn off)"), 'type' => SMOPT_TYPE_INTEGER, 'initial_value' => $frequency, 'refresh' => SMOPT_REFRESH_NONE, 'size' => SMOPT_SIZE_TINY, ); if ($user_can_override_save_frequency_units) $optpage_data['vals']['quicksave'][] = array( 'name' => 'quicksave_units', 'caption' => _("Message Save Units"), 'posvals' => array( 'seconds' => _("Seconds"), 'miliseconds' => _("Miliseconds"), ), 'type' => SMOPT_TYPE_STRLIST, 'initial_value' => $units, 'refresh' => SMOPT_REFRESH_NONE, ); } if ($user_can_override_encryption) $optpage_data['vals']['quicksave'][] = array( 'name' => 'quicksave_encryption', 'caption' => _("Cookie Encryption Level"), 'posvals' => array( 'none' => _("None"), 'low' => _("Low"), 'medium' => _("Medium"), 'moderate' => _("Moderate"), ), 'type' => SMOPT_TYPE_STRLIST, 'initial_value' => $encryption, 'refresh' => SMOPT_REFRESH_NONE, ); if (!empty($optpage_data['vals']['quicksave'])) $optpage_data['grps']['quicksave'] = _("Auto Message Save and Recovery"); sq_change_text_domain('squirrelmail'); } /** * Set flag indicating message was sent * */ function quicksave_message_sent_do($args) { global $javascript_on; if (!$javascript_on) return; $current_hook_name = get_current_hook_name($args); // only use compose_send_after hook when in SM 1.4.6+ // and only use compose_send hook when in SM 1.4.5- // if (check_sm_version(1, 4, 6)) { // even 1.4.6+ needs to work with drafts on the // compose_send hook // if ($current_hook_name == 'compose_send') { global $draft; if ($draft) sqsession_register('sent', 'quicksave_message_sent_status'); return; } // on compose_send_after hook, we have access to information // about whether or not the message was actually sent, // which is very helpful! // if (check_sm_version(1, 5, 2)) $result = $args[0]; else $result = $args[1]; if (!$result) return; } else { if ($current_hook_name == 'compose_send_after') return; } // set "message sent" status flag // sqsession_register('sent', 'quicksave_message_sent_status'); } /** * Turn off quicksave if "message sent" flag is set * */ function quicksave_clear_do() { global $javascript_on; if (!$javascript_on) return; // on compose screen, make sure html_mail has all its // compose_bottom code added before quicksave // global $PHP_SELF; if (stristr($PHP_SELF, 'src/compose.php')) reposition_plugin_on_hook('quicksave', 'compose_bottom', FALSE, 'html_mail'); //TODO: if SM core has $compose_messages purged, won't need qs_cancelled (see below) global $quicksave_message_sent_status, $qs_cancelled, $username; sqGetGlobalVar('quicksave_message_sent_status', $quicksave_message_sent_status, SQ_SESSION); //TODO: if SM core has $compose_messages purged, won't need qs_cancelled (see below) sqGetGlobalVar('qs_cancelled', $qs_cancelled, SQ_FORM); $qs_username = preg_replace('/[\.@_]/', '', $username); // now, deal with the message sent flag // if (!empty($quicksave_message_sent_status) && $quicksave_message_sent_status == 'sent') { $quicksave_message_sent_status = 'not_sent'; sqsession_register($quicksave_message_sent_status, 'quicksave_message_sent_status'); // SM 1.5.2+ no output has gone to browser yet, so // just use PHP to remove quicksave message cache // if (check_sm_version(1, 5, 2)) { sqsetcookie('QS' . $qs_username . 'is_active', 0, 0, '', '', false, false); //TODO: when AJAXified, here is were we can simply wipe message cache from our server-side store } // SM 1.4.x, manage the cookie in JavaScript // else { ?> 0) { $quick_save_return_url = 'read_body.php' . '?passed_id=' . (($forward_id > 0) ? $forward_id : $passed_id) . '&startMessage=' . $startMessage . '&mailbox=' . $urlMailbox . (isset($passed_ent_id) ? '&passed_ent_id=' . $passed_ent_id : '') //TODO: if SM core has $compose_messages purged, won't need qs_cancelled . '&qs_cancelled=1'; } // or if they're replyinging to an email... // elseif ( strpos($action, 'reply') !== FALSE || $reply_id > 0 ) { $quick_save_return_url = 'read_body.php' . '?passed_id=' . (($reply_id > 0) ? $reply_id : $passed_id) . '&startMessage=' . $startMessage . '&mailbox=' . $urlMailbox . (isset($passed_ent_id) ? '&passed_ent_id=' . $passed_ent_id : '') //TODO: if SM core has $compose_messages purged, won't need qs_cancelled . '&qs_cancelled=1'; } // or if they're just composing from anywhere else, we return to the list // else { $quick_save_return_url = 'right_main.php' . '?startMessage=' . $startMessage . '&mailbox=' . $urlMailbox //TODO: if SM core has $compose_messages purged, won't need qs_cancelled . '&qs_cancelled=1'; } sq_change_text_domain('quicksave'); // SM 1.5.2+, just add a button // if (check_sm_version(1, 5, 2)) { global $oTemplate; $nbsp = $oTemplate->fetch('non_breaking_space.tpl'); $output = addButton(_("Cancel"), 'qscancel', array('onclick' => 'quicksave_cancel_button(\'' . $quick_save_return_url . '\'); ' ) ) . $nbsp; sq_change_text_domain('squirrelmail'); return array('compose_button_row' => $output); } // SquirrelMail 1.4.x // else { echo ''; } sq_change_text_domain('squirrelmail'); } /** * Add the code that does all the work onto the compose page * */ function quicksave_compose_functions_do() { global $javascript_on; if (!$javascript_on) return; global $sigappend, $from_htmladdr_search, $restrict_senders_error_no_to_recipients, $restrict_senders_error_too_many_recipients, $compose_new_win, $mail_sent, $restrict_senders_error_too_many_emails_today, $banned_possible_spammer, $quicksave_message_sent_status, $censored, $session_expired; sqGetGlobalVar('sigappend', $sigappend, SQ_FORM); sqGetGlobalVar('from_htmladdr_search', $from_htmladdr_search, SQ_FORM); sqGetGlobalVar('restrict_senders_error_too_many_recipients', $restrict_senders_error_too_many_recipients, SQ_FORM); sqGetGlobalVar('restrict_senders_error_no_to_recipients', $restrict_senders_error_no_to_recipients, SQ_FORM); sqgetGlobalVar('restrict_senders_error_too_many_emails_today', $restrict_senders_error_too_many_emails_today, SQ_FORM); sqgetGlobalVar('banned_possible_spammer', $banned_possible_spammer, SQ_FORM); sqgetGlobalVar('censored', $censored, SQ_FORM); sqGetGlobalVar('mail_sent', $mail_sent, SQ_FORM); sqGetGlobalVar('quicksave_message_sent_status', $quicksave_message_sent_status, SQ_SESSION); sq_change_text_domain('quicksave'); // don't offer any recovery if user just clicked to add // a signature or upload a file or add addresses, etc // or if SquirrelMail's own compose session restore is // doing the job or if our "message has been sent" flag // is on (usually happens when in compose_in_new) // if ($sigappend != 'Signature' && $from_htmladdr_search != 'true' && !$session_expired && $restrict_senders_error_no_to_recipients != 1 && $restrict_senders_error_too_many_recipients != 1 && $restrict_senders_error_too_many_emails_today != 1 && $banned_possible_spammer != 1 && $censored != 1 && empty($_FILES['attachfile']) // NOTE: prior version of quicksave only did the following check if $compose_in_new is turned on or if $mail_sent is 'yes', but technically, if quicksave is working correctly, those are not useful... if this becomes a problem, they could be added back... && (empty($quicksave_message_sent_status) || $quicksave_message_sent_status != 'sent')) { $offer_recovery_OK = TRUE; } else { $offer_recovery_OK = FALSE; $quicksave_message_sent_status = 'not_sent'; // the below causes PHP error in 1.5.2 until 1.5.2 is fixed so compose.php // uses templates for all output; not sure if this happens in 1.4.x too... // plugin might work OK without this...? if (!headers_sent()) sqsession_register($quicksave_message_sent_status, 'quicksave_message_sent_status'); } // figure out where (for use in JavaScript code) the message // body is located - when using HTML_Mail plugin, it isn't // in regular text area // $message_body_location_test = 'if (document.compose.body)'; $message_body_location_restore_test = 'if (document.compose.body)'; $message_body_html = 'document.compose.body.value'; if (is_plugin_enabled('html_mail')) { include_once(SM_PATH . 'plugins/html_mail/functions.php'); if (html_area_is_on_and_is_supported_by_users_browser()) { global $editor_style, $allow_change_html_editor_style; hm_get_config(); if ($allow_change_html_editor_style) $editor_style = getPref($data_dir, $username, 'html_editor_style', $editor_style); list($browser, $browserVersion) = getBrowserType(); // FCKeditor // if ($editor_style == 1) { // IE // if ($browser == 'Explorer' && $browserVersion >= 5.5) { $message_body_location_test = 'if (document.frames[0].document.frames[0].document.body)'; $message_body_location_restore_test = 'if (document.frames[0].document.frames[0].document.body)'; $message_body_html = 'document.frames[0].document.frames[0].document.body.innerHTML'; } // Gecko // else if ($browser == 'Gecko' && $browserVersion >= 20030624) { $message_body_location_test = 'if (document.getElementsByTagName("iframe").item(0).contentDocument.getElementsByTagName("iframe").item(0).contentDocument.body)'; $message_body_location_restore_test = 'if (0)'; $message_body_html = 'document.getElementsByTagName("iframe").item(0).contentDocument.getElementsByTagName("iframe").item(0).contentDocument.body.innerHTML'; // supposedly these work for updated fckeditor versions // $message_body_location_test = 'if(document.getElementById("body___Frame").contentDocument.getElementById("xEditingArea").getElementsByTagName("iframe")[0].contentDocument.body)'; // $message_body_html = 'document.getElementById("body___Frame").contentDocument.getElementById("xEditingArea").getElementsByTagName("iframe")[0].contentDocument.body.innerHTML'; } } // HTMLArea editor // else if ($editor_style == 2) { // IE // if ($browser == 'Explorer' && $browserVersion >= 5.5) { $message_body_location_test = 'if (document.frames[0].document.body)'; $message_body_location_restore_test = 'if (0)'; $message_body_html = 'document.frames[0].document.body.innerHTML'; } // Gecko // else if ($browser == 'Gecko' && $browserVersion >= 20030624) { $message_body_location_test = 'if (document.getElementsByTagName("iframe").item(0).contentDocument.body)'; //FIXME: which is best? both the next two lines seem to work *sometimes*... shrug //$message_body_location_restore_test = 'if (document.getElementsByTagName("iframe").item(0).contentDocument.body)'; $message_body_location_restore_test = 'if (0)'; $message_body_html = 'document.getElementsByTagName("iframe").item(0).contentDocument.body.innerHTML'; } } } } global $quicksave_cookie_days, $quicksave_cookie_hours, $quicksave_cookie_minutes, $maxCookieLength, $maxCookies, $useMultipleCookies, $maxSingleCookieLength, $username, $data_dir, $plugins, $default_cookie_encryption, $user_can_override_encryption; $qs_username = preg_replace('/[\.@_]/', '', $username); include_once(SM_PATH . 'plugins/quicksave/common_functions.php'); quicksave_init(); $encryption = $default_cookie_encryption; if ($user_can_override_encryption) $encryption = getPref($data_dir, $username, 'quicksave_encryption', $encryption); $cookie_time_miliseconds = ($quicksave_cookie_days * 24 * 60 * 60 * 1000) + ($quicksave_cookie_hours * 60 * 60 * 1000) + ($quicksave_cookie_minutes * 60 * 1000); // begin building JavaScript output // $output = "\n\n" . "\n" . "\n\n"; // send output to browser // if (check_sm_version(1, 5, 2)) { // for now, there is no template needed because this // is all just javascript without formatting... also, // the compose_bottom hook in 1.5.2+ is such that it // currently is just like 1.4.x where output goes out // to browser right here (it's not an in-template hook // for example) // echo $output; } else echo $output; sq_change_text_domain('squirrelmail'); } quicksave/common_functions.php0000644000000000000000000000272011327105273015642 0ustar rootroot * Copyright (c) 2003-2010 Paul Lesniewski * Licensed under the GNU GPL. For full terms see the file COPYING. * * @package plugins * @subpackage quicksave * */ /** * Initialize this plugin (load config values) * * @return boolean FALSE if no configuration file could be loaded, TRUE otherwise * */ function quicksave_init() { if (!@include_once (SM_PATH . 'config/config_quicksave.php')) if (!@include_once (SM_PATH . 'plugins/quicksave/config.php')) if (!@include_once (SM_PATH . 'plugins/quicksave/config_default.php')) return FALSE; return TRUE; } /** * Validate that this plugin is configured correctly * * @return boolean Whether or not there was a * configuration error for this plugin. * */ function quicksave_check_configuration_do() { // only need to do this pre-1.5.2, as 1.5.2 will make this // check for us automatically // if (!check_sm_version(1, 5, 2)) { // try to find Compatibility, and then that it is v2.0.7+ // if (function_exists('check_plugin_version') && check_plugin_version('compatibility', 2, 0, 7, TRUE)) return FALSE; // something went wrong // do_err('Quick Save plugin requires the Compatibility plugin version 2.0.7+', FALSE); return TRUE; } } quicksave/docs/0000755000000000000000000000000011332004005012463 5ustar rootrootquicksave/docs/index.php0000644000000000000000000000072211327105317014320 0ustar rootroot Copyright (c) 2003-2010 Paul Lesniewski Description =========== This plugin automatically saves email messages as users compose them. This prevents messages from being accidentally wiped out by refreshing the browser window or otherwise leaving the compose page. Users shouldn't ever know that it's installed unless they've lost a message and it can be recovered. Cookies are used as the storage method for saving the values from the compose form, and an asynchronous JavaScript (aka AJAX) auto-save mechanism is under development (and will alleviate the storage limit that the cookie mechanism imposes). Recovery happens no matter how the user leaves the compose screen, including completely refreshing the whole window (as in a Netscape window resize) or even closing the browser. However, the cookie RFC restricts the size and number of cookies allowed per domain, and thus long messages will only be partially recovered until the AJAX mechanism is implemented. Cookies are kept on a per-user basis, and are encrypted as best they can be using JavaScript. They are persistent, the time for which is configurable (see below). Older systems can be noticably slow when encrypting the user's email, and because encrypted messages need more storage space, using encryption will cut down on the length of any messages that may be recovered. Therefore, the default is no encryption -- the user can increase encryption levels by going to Options->Display Preferences. This plugin does work with 'compose in new window', but PLEASE BE SURE NOT TO DOWNLOAD the 'compose in new window' plugin, since it has already been made part of the SquirrelMail core -- if you download that plugin, which is out of date, QuickSave will not function properly. QuickSave also works with the HTML Mail plugin, although it can be a bit quirky, which is unavoidable when writing HTML-formatted emails. This plugin also will not recover attachments (although this is on the TODO list for future development). License ======= This plugin is released under the GNU General Public License (see COPYING for details). Donations ========= If you or your company make regular use of this software, please consider supporting Open Source development by donating to the authors or inquire about hiring them to consult on other projects. Donation links for the author(s) are as follows: Paul Lesniewski: http://squirrelmail.org/donate_paul_lesniewski.php Requirements ============ * SquirrelMail version 1.2.9 or above * Compatibility plugin version 2.0.7 or above Optional Configuration ====================== Cookies are kept on users' machines for a default of one hour (the rationale being that they know when they lose a message, and shouldn't need more than an hour to recover it; QuickSave is not a replacement for "Save Draft" functionality), but you can change this as desired by changing the values for $quicksave_cookie_days, $quicksave_cookie_hours, and/or $quicksave_cookie_minutes in config.php. config.php also contains settings for restricting the amount of data stored in cookies; values are about maxed out for IE6, default security and privacy settings... If you experience blank recoveries or "must be logged in" messages, you can adjust these downward (the main value you'll want to change is $maxSingleCookieLength). Help Requests ============= Before looking for help elsewhere, please try to help yourself: * Look to see if others have already asked about the same issue. There are tips and links for the best places to do this in the SquirrelMail mailing list posting guidelines: http://squirrelmail.org/wiki/MailingListPostingGuidelines You should also try Google or some other search engine. * If you cannot find any information about your issue, please first mail your help request to the squirrelmail-plugins mailing list. Information about it can be found here: http://lists.sourceforge.net/mailman/listinfo/squirrelmail-plugins You MUST read the mailing list posting guidelines (see above) and include as much information about your issue (and your system) as possible. Including configtest output, any debug output, the plugin configuration settings you've made and anything else you can think of to make it easier to diagnose your problem will get you the most useful responses. Inquiries that do not comply with the posting guidelines are liable to be ignored. * If you don't get any replies on the mailing list, you are welcome to send a help request to the authors' personal address(es), but please be patient with the mailing list. TODO ==== * Add routine to check for no user activity, and after some configured value of time (several minutes? zero to disable functionality), auto-click the "Save Draft" button -- this will save the full message w/out an AJAX solution, but three problems: how to delete draft if user eventually sends the message, not to mention the fact that when saved, the compose screen seems to come back empty and how to recover draft instead of cookie when user later returns... also will too many drafts build up? Maybe that's OK (a good thing)? * AJAX-ify the storage mechanism (storage limits don't apply!) * Provice RPC interface to functions such as whether or not a recoverable message exists (which returns the message data?) and for saving the message (same as above item?) * Recover attachments by snagging the attachment file names from the compose session, although note to end users that if the file gets cleaned up (ie., by cron) before the recovery, the attachment will be lost * Possibly allow message saving to happen when user stops typing? * Show "message saved" on screen after every save? This might be best left for advanced templates and possibly only after the above item is implemented, if at all * If a message that was a reply to another is recovered, I think the fact that it is a reply is lost... is there a way to recover the reply ID? How about for forwards or resumed drafts? * If a message is waiting to be recovered, and the user goes to the compose screen by doing something such as replying, forwarding, or resuming a draft, I think after the recovery is chosen by the user, the reply ID or forward ID or fact that the draft is being resumed might still be the same, when in fact the message now being edited is perhaps unrelated to the original action. Is there a way to clear out that information? Worst case scenario is that a saved draft is then lost. * There is a nice DES encryption mechanism here: http://www.tero.co.uk/des/code.php but it might be too CPU intensive for repeated use in the way this plugin works Change Log ========== 2.4.5 2010/02/03 Paul Lesniewski * Minor fix for rare PHP notices in login routine 2.4.4 2010/01/25 Paul Lesniewski * Bug fix in moderate encryption routine that would render plugin functionality unusable for certain usernames. Although related to the same code, this is a DIFFERENT fix from what was done in version 2.4.3. 2.4.3 2009/12/08 Paul Lesniewski * Bug fix in moderate encryption routine that would render plugin functionality unusable for certain usernames. Thanks to Wolfgang Breyha. 2.4.2 2007/08/26 Paul Lesniewski * Don't offer recovery when SquirrelMail does it natively * Fix fatal error upon login under 1.5.2+ 2.4.1 2007/08/01 Paul Lesniewski * Add message recovery alert upon login in MOTD at top of message list * Trim and do not recover whitespace-only messages * Added additional compatibility fix for Restrict Senders and Censor plugins * Clean up SquirrelMail's incomplete compose message cache when cancel button is clicked 2.4 2007/04/05 Paul Lesniewski * Removed hidden form storage mechanism * Default save frequency changed to five seconds and is configurable by the administrator * Recovery decision made more reliably on server in PHP rather than by the client side script * Added ability for admin to configure defaults for save frequency and encryption and turn off display options if desired * Allow admin to indicate that the beginning of the message body should be shown in recovery alerts * Save-draft patch no longer needed * Fixed error popups that occurred when moderate encryption was enabled * Correct internationalization * Recovery much more reliable when using HTML Mail plugin (only unreliable recovery seems to be with HTMLArea editor under Gecko browsers) * Lots of general cleanup * Uses new sq_change_text_domain() for correct i18n * Reduced added code by about 5kb 2.3 2003/09/12 Paul Lesniewski * Updated for new SM version reporting API. * Updated for compatibility with HTML Mail plugin... note that it is quirky in this context (IE sometimes takes a couple minutes to start saving, no idea why except that this much javascript can bring down the most stable of browsers...). For best results, please make sure that QuickSave is installed *AFTER* the HTML Mail plugin! [2003.Mar.13] 2.2: Fix for javascript problem with comparing multi- line message bodies that was crashing quicksave and causing browser errors. Fixed false recover alerts when cookie only saved part of a long body (see below, nothing we can do about RFC-imposed cookie limits). [2003.Mar.04] 2.1: Minor (optional) fix in the form of a source code patch is included if you don't like the "auto- draft recovery" "feature" (bug) where QuickSave gives false recover alerts after the "Save Draft" button is pressed. [2003.Mar.01] 2.0: Added encryption of cookie data and made cookies persistent and per-user; that is users can recover messages even if their browser crashes, and they won't ever get their stored messages mixed up with another users'. Encryption is not unbreakable, however! Encryption is turned off by default, because it slows down message composing somewhat (depending on the computer and level of encryption) and reduces the length of saved messages considerably. Users can enable the desired level of encryption from the Options-->Display Preferences screen. The sysadmin may can configure the length of time that cookies are kept on users' machines - see config.php for the appropriate settings. config.php also contains settings for restricting the amount of data stored in cookies; values are about maxed out for IE6, default security settings... If you experience blank recoveries or "must be logged in" messages, you can adjust these downward (the main value you'll want to change is $maxSingleCookieLength). Also fixed false recovery alert when adding a signature or getting addresses or attachments; there isn't a good way to avoid attempted recovery when "Save Draft" is pressed, but I say it's good enough for now (sort of like auto-draft recovery ;->) Included in these changes are fixes for problems introduced in version 1.2.0 that prevented recovery between login sessions. Updated to work with SquirrelMail 1.4. Finally, setup.php is in a new format, which helps speed up overall SquirrelMail performance. [2003.Feb.11] 1.2.0: Added user controls for quicksave frequency (and the option to turn it off). Changed functionality so that quicksave will not think the message has been sent until it is really sent, instead of turning itself off when the "Send" button is pressed. Fix for signatures with quotation marks in them that was causing javascript errors. [2002.Nov.12] 1.1.4: When "Compose In New Window" is turned on, the CANCEL button will now close the window (complements Bruce ). [2002.Nov.03] 1.1.3: User signatures are recognized as being different from a message body with actual message text in it, so false alerts when using a signature have been fixed. (Lots of thanks to Nick Tan for helping track down and test this!) All output strings have been internationalized (as of now, there is not a quicksave domain (if someone thinks this is appropriate, it can be done easily), so the translations should be updated in the squirrelmail domain -- see the .po file in the quicksave directory for the strings you need to translate). Updated for compatibility with the Plugin Update plugin. [2002.Aug.10] 1.1.2: Very minor changes: new pop-up text when restoring message and fix such that quicksave doesn't try to restore messages that had no content (if user accidentally presses "Compose" twice). [2002.May.10] 1.1.1: Added a ton more javascript fixes, focused around the problems we had when the Compose page was in its own window. So, to put it best, QuickSave should now be fully compliant with both the New Window and Compose In New Window plugins. Also added a fix for folks like myself who use the pagination feature (thanks to Cor Bosman for that). Finally, the Cancel button now pseudo-intelligently returns you to the message list if you'd clicked 'Compose', or back to your message if you'd clicked Forward or Reply (or Reply All). [2002.Mar.06] 1.1.0: Javascript bug fixes for our IE friends, plus fixes for all those wierd folks out there not using short tags and asp-style prints. (thanks to Seth Randall for making this update really easy) [2001.Apr.30] 1.0.0: Minor bug fixes to the old, form-based storage system. That's not the big thing, though. We now use cookies as the primary storage system. If cookies are being refused (then Squirrel shouldn't work, but I digress), then we still have all the old functionality of the 0.x releases. 0.6: IE over-save bug FINALLY fixed (thanks DJ!), cancel button added to Compose's button row, plus cleaned up recovery process. 0.4, 0.5: not officially released. Attempted fixed for IE's annoying over-save bug (didn't fix it). With 0.5, canceling works better. 0.3: only fields which have values will be saved and/or restored. 0.2: bug fixes, requirements updated. 0.1: initial release (duh). quicksave/index.php0000644000000000000000000000072211327105326013370 0ustar rootroot
'; if ($show_message_details_in_motd) echo '
' . _("NOTE: The following email was interrupted and was never sent:") . '
  ' . _("To:") . ' ' . $send_to_contents . '
  ' . _("Subject:") . ' ' . $subject_contents . (!empty($body_contents) ? '
' : '') . $body_contents . '
' . $compose_uri . '
'; else echo '
' . _("NOTE: You have an unsent message that was interrupted.") . '
' . $compose_uri . '
'; quicksave/config_default.php0000644000000000000000000000656411327156245015252 0ustar rootroot SquirrelMail QuickSave Plugin Translation File # Copyright (c) 2007 The Squirrelmail Development Team # This file is distributed under the same license as the SquirrelMail package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: quicksave\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2007-08-01 10:20-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: SquirrelMail Language Team \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "Auto Message Save and Recovery" msgstr "" msgid "Message Save Frequency" msgstr "" msgid "(set to zero to turn off)" msgstr "" msgid "Message Save Units" msgstr "" msgid "Seconds" msgstr "" msgid "Miliseconds" msgstr "" msgid "Cookie Encryption Level" msgstr "" msgid "None" msgstr "" msgid "Low" msgstr "" msgid "Medium" msgstr "" msgid "Moderate" msgstr "" msgid "Cancel" msgstr "" msgid "QuickSave Error - No encryption password given. Please contact your system administrator." msgstr "" msgid "QuickSave Error - Algorithm cannot find a suitable hash; bad password.\\nPlease contact your system administrator." msgstr "" msgid "QuickSave Error - A salt value could not be extracted from the encrypted message\\nbecause its length is too short. The message cannot be decrypted.\\nPlease contact your system administrator." msgstr "" msgid "QuickSave Error - No decryption password given. Please contact your system administrator." msgstr "" msgid "" msgstr "" msgid "WARNING: The following email was interrupted and was never sent!" msgstr "" msgid "To:" msgstr "" msgid "Subject:" msgstr "" msgid "Do you wish to resume it? (Press cancel to discard message)" msgstr "" msgid "Email restored!" msgstr "" msgid "Please remember to press Send when finished typing your message." msgstr "" msgid "NOTE: The following email was interrupted and was never sent:" msgstr "" msgid "Click here to resume it." msgstr "" msgid "NOTE: You have an unsent message that was interrupted." msgstr "" quicksave/locale/.htaccess0000644000000000000000000000001611307451571014605 0ustar rootrootDeny from All quicksave/locale/getpot0000744000000000000000000000057311307446620014244 0ustar rootroot#!/bin/sh XGETTEXT_OPTIONS="--keyword=_ -keyword=N_ --default-domain=quicksave --add-comments=i18n" # Allows controlling language option # (gettext v.0.10.40 = -C, gettext 0.11+ = -L php). if [ $SM_OLD_GETTEXT ] ; then XGETTEXT_OPTIONS="${XGETTEXT_OPTIONS} -C"; else XGETTEXT_OPTIONS="${XGETTEXT_OPTIONS} -L php"; fi xgettext ${XGETTEXT_OPTIONS} *.php --output=quicksave.pot quicksave/version0000644000000000000000000000002111332004022013133 0ustar rootrootQuick Save 2.4.5 quicksave/config_example.php0000644000000000000000000000656411307450661015256 0ustar rootroot\n"; if ($save_as_draft) { - echo '\n"; + echo '\n"; } do_hook('compose_button_row'); quicksave/patches/compose.php-draft_saved-1.4.2.diff0000644000000000000000000000073410152306264021206 0ustar rootroot--- src/compose.php.orig Fri Sep 12 22:36:04 2003 +++ src/compose.php Fri Sep 12 22:36:18 2003 @@ -1232,7 +1232,7 @@ } if ($save_as_draft) { - echo ' \n"; + echo ' \n"; } if ($location_of_buttons != 'bottom') { quicksave/patches/compose.php-draft_saved-1.4.0.diff0000644000000000000000000000100610152306264021175 0ustar rootroot--- src/compose.php.old Tue Mar 4 17:17:26 2003 +++ src/compose.php Tue Mar 4 17:17:51 2003 @@ -1129,7 +1129,7 @@ } if ($save_as_draft) { - echo ' \n"; + echo ' \n"; } echo ' ' . "\n"; quicksave/patches/compose.php-draft_saved-1.2.10.diff0000644000000000000000000000101110152306264021250 0ustar rootroot--- src/compose.php.old Tue Nov 19 22:49:39 2002 +++ src/compose.php Tue Mar 4 17:13:39 2003 @@ -888,7 +888,7 @@ echo "\n \n"; if ($save_as_draft) { - echo '\n"; + echo '\n"; } do_hook('compose_button_row'); quicksave/setup.php0000644000000000000000000001565711332004022013421 0ustar rootroot * Copyright (c) 2003-2010 Paul Lesniewski * Licensed under the GNU GPL. For full terms see the file COPYING. * * @package plugins * @subpackage quicksave * */ /** * Register this plugin with SquirrelMail * */ function squirrelmail_plugin_init_quicksave() { global $squirrelmail_plugin_hooks; $squirrelmail_plugin_hooks['compose_bottom']['quicksave'] = 'quicksave_compose_functions'; $squirrelmail_plugin_hooks['generic_header']['quicksave'] = 'quicksave_clear'; $squirrelmail_plugin_hooks['configtest']['quicksave'] = 'quicksave_check_configuration'; // SquirrelMail 1.4.x (although the first two hooks here // also have a touch of 1.5.x code in them too) // $squirrelmail_plugin_hooks['right_main_after_header']['quicksave'] = 'quicksave_check_recovery_upon_login'; $squirrelmail_plugin_hooks['compose_send']['quicksave'] = 'quicksave_message_sent'; $squirrelmail_plugin_hooks['compose_button_row']['quicksave'] = 'quicksave_cancel_button'; $squirrelmail_plugin_hooks['optpage_loadhook_display']['quicksave'] = 'quicksave_options_14'; // SquirrelMail 1.5.x // $squirrelmail_plugin_hooks['template_construct_motd.tpl']['quicksave'] = 'quicksave_check_recovery_upon_login'; $squirrelmail_plugin_hooks['compose_send_after']['quicksave'] = 'quicksave_message_sent'; $squirrelmail_plugin_hooks ['template_construct_compose_buttons.tpl']['quicksave'] = 'quicksave_cancel_button'; $squirrelmail_plugin_hooks['optpage_loadhook_compose']['quicksave'] = 'quicksave_options_15'; } /** @ignore */ if (!defined('SM_PATH')) define('SM_PATH', '../'); /** * Returns info about this plugin * */ function quicksave_info() { return array( 'english_name' => 'Quick Save', 'authors' => array( 'Paul Lesniewski' => array( 'email' => 'paul@squirrelmail.org', 'sm_site_username' => 'pdontthink', ), ), 'summary' => 'Automatically saves messages as they are being composed to prevent accidental loss due to leaving the compose screen or browser/computer crashes.', 'details' => 'This plugin automatically saves messages as they are being composed in order to prevent accidental loss of message content due to having browsed away from the compose screen or more serious problems such as browser or computer crashes. When a message appears to have been lost and is available for recovery, the user will be prompted about whether or not the recovery should proceed.', 'version' => '2.4.5', 'required_sm_version' => '1.2.9', 'requires_configuration' => 0, 'requires_source_patch' => 0, // NOTE: could just require Compatibility 2.0.7 for all SM versions, // but the following is intended as an example for how plugin // authors can indicate different requirements for different // SM versions. Also note that other requirements such as // "requires_configuration" may also be overridden in the same // fashion. // FYI, when SM looks for one of these requirement values, it // takes the highest one it can find (so for example, when // running SM 1.5.2, and the needed requirement value is only // listed here under 1.2.9 and 1.4.10, it will take the value // from the 1.4.10 settings), or will use the "global" value // above if not found herein. 'per_version_requirements' => array( '1.5.2' => array( 'requires_source_patch' => 0, 'required_plugins' => array( 'compatibility' => array( 'version' => '2.0.5', 'activate' => FALSE, ) ) ), '1.5.0' => array( 'requires_source_patch' => 0, 'required_plugins' => array( 'compatibility' => array( 'version' => '2.0.7', 'activate' => FALSE, ) ) ), '1.4.10' => array( 'requires_source_patch' => 0, 'required_plugins' => array( 'compatibility' => array( 'version' => '2.0.5', 'activate' => FALSE, ) ) ), '1.2.9' => array( 'requires_source_patch' => 0, 'required_plugins' => array( 'compatibility' => array( 'version' => '2.0.7', 'activate' => FALSE, ) ) ), ), ); } /** * Returns version info about this plugin * */ function quicksave_version() { $info = quicksave_info(); return $info['version']; } /** * Determine whether or not to offer message * recovery upon login * */ function quicksave_check_recovery_upon_login($args) { include_once(SM_PATH . 'plugins/quicksave/login_functions.php'); return quicksave_check_recovery_upon_login_do($args); } /** * Present user quicksave preferences on display/compose * options page for SquirrelMail 1.5.x * */ function quicksave_options_15() { include_once(SM_PATH . 'plugins/quicksave/functions.php'); quicksave_options_15_do(); } /** * Present user quicksave preferences on display/compose * options page for SquirrelMail 1.4.x * */ function quicksave_options_14() { include_once(SM_PATH . 'plugins/quicksave/functions.php'); quicksave_options_14_do(); } /** * Set flag indicating message was sent * */ function quicksave_message_sent($args) { include_once(SM_PATH . 'plugins/quicksave/functions.php'); quicksave_message_sent_do($args); } /** * Turn off quicksave if "message sent" flag is set * */ function quicksave_clear() { include_once(SM_PATH . 'plugins/quicksave/functions.php'); quicksave_clear_do(); } /** * Add "cancel" button to the compose page * */ function quicksave_cancel_button() { include_once(SM_PATH . 'plugins/quicksave/functions.php'); return quicksave_cancel_button_do(); } /** * Add the code that does all the work onto the compose page * */ function quicksave_compose_functions() { include_once(SM_PATH . 'plugins/quicksave/functions.php'); quicksave_compose_functions_do(); } /** * Validate that this plugin is configured correctly * */ function quicksave_check_configuration() { include_once(SM_PATH . 'plugins/quicksave/common_functions.php'); return quicksave_check_configuration_do(); } quicksave/README0000644000000000000000000000006511307450344012431 0ustar rootrootPlease see all documentation in the docs/ directory. quicksave/make_release.sh0000744000000000000000000001346311327105234014526 0ustar rootroot#!/bin/sh # Generic shell script for building SquirrelMail plugin release # # Copyright (c) 2004-2010 Paul Lesniewski # Licensed under the GNU GPL. For full terms see the file COPYING. # ####################################################### # # CONFIGURATION # # Relative paths to any and all configuration files # for this plugin: these files will NOT be included # in the release package built by this script; they # should be given as relative paths and filenames from # the plugin's own directory - for example, if you # have a config.php file in the main plugin directory # and a special_config.php file in a "data" subdirectory, # this should be set as follows: # # CONFIG_FILES=( config.php data/special_config.php ) # # Note that you can also use this setting to exclude # entire subdirectories while creating the release # package. Here is an example that skips any files # inside a subdirectory called "cache_files" and # completely removes a subdirectory called "tmp", as # well as the standard config.php file: # # CONFIG_FILES=( config.php tmp cache_files/* ) # # CONFIG_FILES=( config.php ) # # END CONFIGURATION # ####################################################### # avoid all kinds of potential problems; only allow # this to be run from directory where it resides # if [ "$0" != "./make_release.sh" ]; then echo echo "Please do not run from remote directory" echo exit 1 fi # grab name of package being built from directory name # # PACKAGE=`echo "$PWD" | sed s/.*\\\///` # get "pretty name" from version file # if [ ! -e version ]; then echo echo "No version file found. Please create before making release" echo exit 2 fi PRETTY_NAME=`head -1 version` # announce ourselves # echo echo "Creating Release Package for $PRETTY_NAME" echo # grab old version number straight from the php code # OLD_VERSION=`echo "" | php -q` REQ_SM_VERSION=`echo "" | php -q` # check for the standard files... # if [ ! -e README ]; then echo echo "No README file found. Please create before making release" echo exit 3 fi if [ ! -e docs/README ]; then echo echo "No docs/README file found. Please create before making release" echo exit 3 fi if [ ! -e docs/INSTALL ]; then echo echo "No docs/INSTALL file found. Please create before making release" echo exit 4 fi if [ ! -e locale/getpot ]; then echo echo "No locale/getpot file found. Please create before making release" echo exit 5 fi if [ ! -e locale/$PACKAGE.pot ]; then echo echo "No locale/$PACKAGE.pot file found. Please create before making release" echo exit 5 fi # just copy index.php and COPYING automatically if not found # if [ ! -e docs/COPYING ]; then echo "No docs/COPYING file found. Grabbing one from ../../" cp ../../COPYING ./docs/ fi if [ ! -e index.php ]; then echo "No index.php file found. Grabbing one from ../" cp ../index.php . fi if [ ! -e docs/index.php ]; then echo "No docs/index.php file found. Grabbing one from ../" cp ../index.php ./docs/ fi if [ ! -e locale/index.php ]; then echo "No locale/index.php file found. Grabbing one from ../" cp ../index.php ./locale/ fi # Make our own docs/.htaccess and locale/.htaccess if needed # if [ ! -e docs/.htaccess ]; then echo "No docs/.htaccess file found. Creating..." echo "Deny from All" > ./docs/.htaccess fi if [ ! -e locale/.htaccess ]; then echo "No locale/.htaccess file found. Creating..." echo "Deny from All" > ./locale/.htaccess fi # remove any previous tarballs # while test 1; do echo echo -n "Remove all .tar.gz files? (y/[n]): " read REPLY if test -z $REPLY; then REPLY="n" break fi if test $REPLY = "y"; then break fi if test $REPLY = "n"; then break fi done if [ "$REPLY" = "y" ]; then rm -f *.tar.gz fi # get new version number if needed # if [ ! -z "$REQ_SM_VERSION" ] ; then OLD_FULL_VERSION=$OLD_VERSION-$REQ_SM_VERSION else OLD_FULL_VERSION=$OLD_VERSION fi echo read -p "Enter Version Number [$OLD_VERSION]: " VERSION if [ -z "$VERSION" ] ; then VERSION=$OLD_VERSION; # VERSION=$OLD_FULL_VERSION; fi PURE_VERSION=`echo "$VERSION" | sed 's/-.*//'` if [ ! -z "$REQ_SM_VERSION" ] ; then FINAL_VERSION="$PURE_VERSION-$REQ_SM_VERSION" else FINAL_VERSION="$PURE_VERSION" fi # remove tarball we are building if present # echo echo "Removing $PACKAGE-$FINAL_VERSION.tar.gz" rm -f $PACKAGE-$FINAL_VERSION.tar.gz # replace version number in info function in setup.php # NOTE that this requires specific syntax in setup.php # for the _info() function which should be # a line that looks like: # 'version' => '', # if test -e setup.php; then echo "Replacing version in setup.php (info function)" sed -e "s/'version' => '$OLD_VERSION',/'version' => '$PURE_VERSION',/" setup.php > setup.php.tmp mv setup.php.tmp setup.php fi # update version number in version file too # echo "Replacing version in version file" echo "$PRETTY_NAME" > version echo $PURE_VERSION >> version # Build tar command; exclude config and other irrelevant files # TAR_COMMAND="tar -c -z -v --exclude CVS --exclude .svn" J=0 while [ "$J" -lt ${#CONFIG_FILES[@]} ]; do echo "Excluding ${CONFIG_FILES[$J]}" TAR_COMMAND="$TAR_COMMAND --exclude ${CONFIG_FILES[$J]}" J=`expr $J + 1` done TAR_COMMAND="$TAR_COMMAND -f $PACKAGE-$FINAL_VERSION.tar.gz $PACKAGE" # make tarball # echo "Creating $PACKAGE-$FINAL_VERSION.tar.gz" cd ../ $TAR_COMMAND mv $PACKAGE-$FINAL_VERSION.tar.gz $PACKAGE cd $PACKAGE echo echo "Finished" echo quicksave/login_functions.php0000644000000000000000000004037511332003675015471 0ustar rootroot * Copyright (c) 2003-2010 Paul Lesniewski * Licensed under the GNU GPL. For full terms see the file COPYING. * * @package plugins * @subpackage quicksave * */ /** * Determine whether or not to offer message * recovery upon login * */ function quicksave_check_recovery_upon_login_do($args) { global $javascript_on; if (!$javascript_on) return FALSE; if (!sqgetGlobalVar('just_logged_in', $just_logged_in, SQ_SESSION) || empty($just_logged_in)) return FALSE; global $show_message_recovery_alert_in_motd, $motd, $show_message_body_on_recover_motd_notice, $default_cookie_encryption, $user_can_override_encryption, $username, $data_dir, $show_message_details_in_motd; include_once(SM_PATH . 'plugins/quicksave/common_functions.php'); quicksave_init(); // don't bother if not enabled // if (!$show_message_recovery_alert_in_motd) return FALSE; if (!cookie_pull('is_active', FALSE)) return FALSE; $encryption = $default_cookie_encryption; if ($user_can_override_encryption) $encryption = getPref($data_dir, $username, 'quicksave_encryption', $encryption); // can't show message details in MOTD until we fix the // PHP transcription of the decryption function for the // medium level // if ($encryption == 'medium') $show_message_details_in_motd = FALSE; // get all compose fields from cookie // $send_to_contents = trim(cookie_pull('send_to', TRUE)); $send_to_cc_contents = trim(cookie_pull('send_cc', TRUE)); $send_to_bcc_contents = trim(cookie_pull('send_bcc', TRUE)); $subject_contents = trim(cookie_pull('subject', TRUE)); $body_contents = trim(cookie_pull('body', TRUE)); // only offer to restore if there was any data there // if (empty($send_to_contents) && empty($send_to_cc_contents) && empty($send_to_bcc_contents) && empty($subject_contents) && empty($body_contents)) return FALSE; // format strings to be shown in alert //TODO: length limitations below are from JavaScript; probably need to fine-tune them for fitting in the MOTD area (longer) // sq_change_text_domain('quicksave'); if (strlen($send_to_contents) > 40) $send_to_contents = substr($send_to_contents, 0, 35) . '...'; else if (empty($send_to_contents)) $send_to_contents = _(""); if (strlen($subject_contents) > 50) $subject_contents = substr($subject_contents, 0, 45) . '...'; else if (empty($subject_contents)) $subject_contents = _(""); if ($show_message_body_on_recover_motd_notice) { if (strlen($body_contents) > 80) $body_contents = substr($body_contents, 0, 75) . '...'; } else $body_contents = ''; if (check_sm_version(1, 5, 2)) { global $oTemplate; $oTemplate->assign('show_message_details_in_motd', $show_message_details_in_motd); $oTemplate->assign('motd_pad', (strlen($motd) > 0)); $oTemplate->assign('send_to_contents', $send_to_contents); $oTemplate->assign('send_to_cc_contents', $send_to_cc_contents); $oTemplate->assign('send_to_bcc_contents', $send_to_bcc_contents); $oTemplate->assign('subject_contents', $subject_contents); $oTemplate->assign('body_contents', $body_contents); $oTemplate->assign('compose_uri', makeComposeLink('src/compose.php?qs_recover=1', _("Click here to resume it.")), FALSE); $output = $oTemplate->fetch('plugins/quicksave/motd_recover_alert.tpl'); sq_change_text_domain('squirrelmail'); $motd .= ' '; // trick so other plugins know MOTD has been added to return array('motd_inside' => $output); } else { if ($show_message_details_in_motd) $motd .= (empty($motd) ? '' : '

') . _("NOTE: The following email was interrupted and was never sent:") . '
  ' . _("To:") . ' ' . $send_to_contents . '
  ' . _("Subject:") . ' ' . $subject_contents . (!empty($body_contents) ? '
' : '') . $body_contents . '
' . makeComposeLink('src/compose.php?qs_recover=1', _("Click here to resume it.")); else $motd .= _("NOTE: You have an unsent message that was interrupted.") . '
' . makeComposeLink('src/compose.php?qs_recover=1', _("Click here to resume it.")); } sq_change_text_domain('squirrelmail'); } /** * Try to grab stored quicksave cookie * * @param string $name The name of the cookie to pull * @param boolean $decrypt Whether or not to decrypt the * retrieved value before returning it * * @return string The value found for the requested * cookie, or an empty string if not found * */ function cookie_pull($name, $decrypt) { global $username, $data_dir, $default_cookie_encryption, $user_can_override_encryption, $useMultipleCookies; include_once(SM_PATH . 'plugins/quicksave/common_functions.php'); quicksave_init(); $encryption = $default_cookie_encryption; if ($user_can_override_encryption) $encryption = getPref($data_dir, $username, 'quicksave_encryption', $encryption); $cookie_value = ''; $qs_username = preg_replace('/[\.@_]/', '', $username); // get cookie first // // TODO: urlencode() may not produce same result as the // JavaScript escape() function; may produce anomalies...? // sqGetGlobalVar(urlencode('QS' . $qs_username) . $name, $cookie_value, SQ_COOKIE); $cookie_value = urldecode($cookie_value); // if using multiple cookie storage, try to piece together // more than one cookie // if ($useMultipleCookies) { if (empty($cookie_value)) { $cookieCount = 0; $cookie_value = ''; while (sqGetGlobalVar(urlencode('QS' . $qs_username) . $name . (++$cookieCount), $cookie_crumb, SQ_COOKIE)) { $cookie_value .= urldecode($cookie_crumb); } } } // if no decryption is needed, return immediately // if (!$decrypt || $encryption == 'none') return $cookie_value; // decrypt // switch ($encryption) { case 'low' : return decrypt_low($cookie_value); case 'medium' : return decrypt_medium($cookie_value); case 'moderate' : return decrypt_moderate($cookie_value); default : die('QuickSave encryption level not understood'); } } /** * Decrypt low * * This function is a PHP-transcribed version of the * basic/simple ascii encryption JavaScript function * taken and slightly modified from javascript.com: * http://javascript.internet.com/passwords/character-encoder.html * Original: Mike McGrath (mike_mcgrath@lineone.net) * Web Site: http://website.lineone.net/~mike_mcgrath/ * * @param string $value The value to decrypt * * @return The decrypted value * */ function decrypt_low($value) { if (empty($value)) return ''; $decrypted_string = ''; for ($i = 0; $i < strlen($value); $i += 2) { $num_in = substr($value, $i, 2) + 23; // TODO: urldecode() may not produce same result as // the JavaScript unescape() function; may produce // anomalies...? $num_in = urldecode('%' . base_convert($num_in, 10, 16)); $decrypted_string .= $num_in; } return urldecode($decrypted_string); } /** * Decrypt medium * * This function is a PHP-transcribed version of the * basic/simple ascii encryption JavaScript function * taken and slightly modified from javascript.com: * http://javascript.internet.com/passwords/ascii-encryption.html * Original: David Salsinha (david.salsinha@popsi.pt) * * @param string $value The value to decrypt * * @return The decrypted value * */ function decrypt_medium($value) { //TODO: currently very broken if (empty($value)) return ''; // TODO: urldecode() may not produce same result as // the JavaScript unescape() function; may produce // anomalies...? // In fact, the problem seems rather to be that PHP is // not seeing the same characters in the string that // it grabs from the cookie. JavaScript uses UTF-16 // and PHP UTF-8, and that might be part of the issue; // in any case, PHP seems to decode the cookie very // differently than JavaScript, and unless that can be // fixed, it doesn't look like there's much use in trying // to make up the difference in this function... // below was much mucking about, trying to avoid using // mbstring, etc., but I don't know what I'm doing $value = urldecode($value); $decrypted_string = ''; $temp = array(); $temp2 = array(); $length = strlen($value); for ($i = 0; $i < $length; $i++) { $temp[$i] = ord($value{$i}); $temp2[$i] = isset($value{($i + 1)}) ? ord($value{($i + 1)}) : 0; //temp[$i] = uniord($value{$i}); //temp2[$i] = isset($value{($i + 1)}) ? uniord($value{($i + 1)}) : 0; } for ($i = 0; $i < $length; $i = $i + 2) { $decrypted_string .= chr($temp[$i] - $temp2[$i]); //decrypted_string .= unichr($temp[$i] - $temp2[$i]); } return urldecode($decrypted_string); } /** * Unicode-capable chr() * * Swiped from user comments at: * http://php.net/manual/function.chr.php * */ function unichr($code) { // TODO: not tested if ($code < 128) { $utf = chr($code); } else if ($code < 2048) { $utf = chr(192 + (($code - ($code % 64)) / 64)); $utf .= chr(128 + ($code % 64)); } else { $utf = chr(224 + (($code - ($code % 4096)) / 4096)); $utf .= chr(128 + ((($code % 4096) - ($code % 64)) / 64)); $utf .= chr(128 + ($code % 64)); } return $utf; // TODO: not tested - alternative method // is this a better way? it only works for PHP 4.3.0+ return html_entity_decode('&#' . $code . ';', ENT_NOQUOTES, 'UTF-8'); } /** * Unicode-capable ord() * * Swiped from user comments at: * http://php.net/manual/function.ord.php * * @Algorithm: http://www1.tip.nl/~t876506/utf8tbl.html * @Logic: UTF-8 to Unicode conversion * */ function uniord($char) { $ud = 0; if (ord($char{0})>=0 && ord($char{0})<=127) $ud = ord($char{0}); if (ord($char{0})>=192 && ord($char{0})<=223) $ud = (ord($char{0})-192)*64 + (ord($char{1})-128); if (ord($char{0})>=224 && ord($char{0})<=239) $ud = (ord($char{0})-224)*4096 + (ord($char{1})-128)*64 + (ord($char{2})-128); if (ord($char{0})>=240 && ord($char{0})<=247) $ud = (ord($char{0})-240)*262144 + (ord($char{1})-128)*4096 + (ord($char{2})-128)*64 + (ord($char{3})-128); if (ord($char{0})>=248 && ord($char{0})<=251) $ud = (ord($char{0})-248)*16777216 + (ord($char{1})-128)*262144 + (ord($char{2})-128)*4096 + (ord($char{3})-128)*64 + (ord($char{4})-128); if (ord($char{0})>=252 && ord($char{0})<=253) $ud = (ord($char{0})-252)*1073741824 + (ord($char{1})-128)*16777216 + (ord($char{2})-128)*262144 + (ord($char{3})-128)*4096 + (ord($char{4})-128)*64 + (ord($char{5})-128); if (ord($char{0})>=254 && ord($char{0})<=255) //error $ud = false; return $ud; // TODO: not tested - alternative method // needs PHP 4.1.0 // no, this would need a full translation table to work... $ret = htmlentities($char, ENT_NOQUOTES, 'UTF-8'); $ret = convert_html_entities_to_unicode_entities($ret); return trim($ret, '&#;'); } function convert_html_entities_to_unicode_entities($string, $quote_style=ENT_COMPAT) { $htmlEntities = array_values (get_html_translation_table (HTML_ENTITIES, ENT_QUOTES)); $entitiesDecoded = array_keys (get_html_translation_table (HTML_ENTITIES, ENT_QUOTES)); $num = count ($entitiesDecoded); for ($u = 0; $u < $num; $u++) { $utf8Entities[$u] = '&#'.ord($entitiesDecoded[$u]).';'; } return str_replace ($htmlEntities, $utf8Entities, $string); // TODO: not tested - alternative method $trans = get_html_translation_table(HTML_ENTITIES, $quote_style); foreach ($trans as $key => $value) $trans[$key] = '&#' . ord($key) . ';'; return strtr($string, $trans); } /** * Decrypt moderate * * This function is a PHP-transcribed version of the * XOR encryption JavaScript function taken from * javascript.com: * http://javascript.internet.com/passwords/xor-encryption4.html * Copyright 2001 by Terry Yuen. * Email: kaiser40@yahoo.com * Last update: July 15, 2001 * * @param string $value The value to decrypt * * @return The decrypted value * */ function decrypt_moderate($value) { if (empty($value)) return ''; global $username; $pwd = $username; if (strlen($value) < 5) { die(_("QuickSave Error - A salt value could not be extracted from the encrypted message\\nbecause its length is too short. The message cannot be decrypted.\\nPlease contact your system administrator.")); } if (empty($pwd)) { die(_("QuickSave Error - No decryption password given. Please contact your system administrator.")); } $prand = ''; for ($i = 0; $i < strlen($pwd); $i++) { $prand .= ord($pwd{$i}); //TODO: not sure if we will lose (UTF) encoding here... // was: prand += pwd.charCodeAt(i).toString(); // $prand .= uniord($pwd{$i}); } $divisor = 5; while ($divisor > 1) { $mult = ''; $sPos = floor(strlen($prand) / $divisor); $found_non_zero = FALSE; for ($i = 1; $i <= $divisor; $i++) { $char = $prand{($sPos > 0 ? $sPos * $i - 1 : 0)}; if ($char != '0' && $char != '') { $found_non_zero = TRUE; } $mult .= $char; } if ($found_non_zero) break; $divisor--; } $incr = round(strlen($pwd) / 2); $modu = pow(2, 31) - 1; $salt = base_convert(substr($value, strlen($value) - 8), 16, 10); $value = substr($value, 0, strlen($value) - 8); $prand .= $salt; // I think the original JavaScript is borked; the first time it // does this math, it correctly takes the full numbers and adds // them, but after that, it truncates the numbers after either // a decimal point or the scientific notator "e"; so this (and // the while loop below that uses intval()) is a hack to match // that behavior (well, there is one more hitch, which is that // PHP's precision doesn't quite match JavaScript's, so we try // to use gmp_add() to rectify that, but if not available (ala // PHP 4 on Windows), then we use an even more horrible hack, // where we just make sure the number is big enough and know that // the addition won't usually change the result, which will be // the rounded value of digits 10 thru 25) // // 50 is somewhat arbitrary // if (strlen($prand) > 10) { if (function_exists('gmp_add')) $prand = gmp_strval(gmp_add(substr($prand, 0, 10), substr($prand, 10))); else if (strlen($prand) > 50) $prand = substr($prand, 10, 17); // manual rounding. yuck // if (isset($prand{16})) $prand = $prand{0} . '.' . substr($prand, 1, 14) . ($prand{16} > 4 ? $prand{15} + 1 : $prand{15}); } while (strlen($prand) > 10) { $prand = intval(substr($prand, 0, 10)) + intval(substr($prand, 10)); } // not enough precision in modulus operation, so we do it manually: //$prand = ($mult * $prand + $incr) % $modu; $div = (int)((($mult * $prand) + $incr) / $modu); $prand = (($mult * $prand) + $incr) - ($div * $modu); $enc_chr = ''; $enc_str = ''; for ($i = 0; $i < strlen($value); $i += 2) { $enc_chr = base_convert(substr($value, $i, 2), 16, 10) ^ floor(($prand / $modu) * 255); $enc_str .= chr($enc_chr); //TODO: not sure if we will lose (UTF) encoding here... // was: enc_str += String.fromCharCode(enc_chr); // $enc_str .= unichr($enc_chr); // not enough precision in modulus operation, so we do it manually: //$prand = ($mult * $prand + $incr) % $modu; $div = (int)((($mult * $prand) + $incr) / $modu); $prand = (($mult * $prand) + $incr) - ($div * $modu); } return urldecode($enc_str); }