Page MenuHomePhorge

No OneTemporary

Size
71 KB
Referenced Files
None
Subscribers
None
diff --git a/CHANGELOG b/CHANGELOG
index da194bb05..4988b49c1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,608 +1,609 @@
CHANGELOG RoundCube Webmail
---------------------------
-2007/03/01 (thomasb)
+2007/03/02 (thomasb)
----------
+- Show "no subject" in message list if subject is missing (closes #1484243)
- Solved page caching of message preview (closes #1484153)
- Only use gzip compression if configured (closes #1484236)
2007/02/25 (estadtherr)
----------
- Fixed priority selector issue (#1484150)
- Upgraded to TinyMCE v2.1.0
2007/02/21 (thomasb)
----------
- Fixed some CSS issues in default skin (closes #1484210 and #1484161)
- Prevent from double quoting of numeric HTML character references (closes #1484253)
2007/02/07 (tomekp)
----------
- Updated (bg) translation (Doichin Dokov)
2007/02/06 (tomekp)
----------
- Updated (pl) translation
- Updated (pt_BR) translation (Robson F. Ramaldes)
- Big cleanup in program/localization
2007/02/05 (thomasb)
----------
- Updated Italian, Slovenian, Greek, Bulgarian, Hungarian and Croatian translation
2007/01/07 (estadtherr)
----------
- Fixed display of HTML message attachments (closes #1484178)
2007/01/07 (thomasb)
----------
- Applied patch for preview caching (closes #1484186)
- Added Thai and Vietnamese localization files
2006/12/29 (thomasb)
----------
- Added error handling for attachment uploads
- Use multibyte safe string functions where necessary (closes #1483988)
- Updated Swiss German localization (de_CH)
2006/12/22 (thomasb)
----------
- Applied security patch to validate the submitted host value (by Kees Cook)
- Applied security patch to validate input values when deleting contacts (by Kees Cook)
- Applied security patch that sanitizes emoticon paths when attaching them (by Kees Cook)
- Applied a patch to more aggressively sanitize a HTML message
- Visualize blocked images in HTML messages
2006/12/20 (thomasb)
----------
- Fixed wrong message listing when showing search results (closes #1484131)
- Introduced functions Q() and JQ() as aliases for rep_specialchars_output()
- Show remote images when opening HTML message part as attachment
2006/12/17 (thomasb)
----------
- Added patch by Ryan Rittenhouse & David Glick for a resizeable preview pane
2006/12/06 (thomasb)
----------
- Improve memory usage when sending mail (closes #1484098)
- Mark messages as read once the preview is loaded (closes #1484132)
- Include smtp final response in log (closes #1484081)
2006/12/04 (thomasb)
----------
- Corrected date string in sent message header (closes #1484125)
- Correclty choose "To" column in sent and draft mailboxes (closes #1483943)
- Changed srong tooltips for message browse buttons (closes #1483930)
2006/12/03 (estadtherr)
----------
- Added fix to convert HTML signatures for plain text messages
- Fixed signature delimeter character to be standard (Bug #1484035)
2006/12/01 (thomasb)
----------
- Implemented preview pane
- Fixed XSS vulnerability (Bug #1484109)
- Remove newlines from mail headers (Bug #1484031)
- Selection issues when moving/deleting (Bug #1484044)
- Applied patch of Clement Moulin for imap host auto-selection
- ISO-encode IMAP password for plaintext login (Bugs #1483977 & #1483886)
- Fixed folder name encoding in subscription list (Bug #1484113)
- Fixed JS errors in identity list (Bug #1484120)
- Show client debug console on debug_level 8
- Added Serbian translation
- Updated Spanish and Bulgarian localization
2006/11/22 (robin)
----------
- Fix a bug introduced with Shift-Del yesterday
2006/11/21 (robin)
----------
- Add missing nl_NL translations
- Translate foldernames in folder form (closes #1484113)
2006/11/21 (robin)
----------
- Added first and last buttons to message list, address book
and message detail
- Pressing Shift-Del bypasses Trash folder
- Enable purge command for Junk folder
2006/11/17 (robin)
----------
- Re-initialize message list after shift-select and delete
2006/11/16 (robin)
----------
- Fixed updating message list after expunge and purge
- Fetch all aliases if virtuser_query is used instead
of only the first one
2006/11/11 (estadtherr)
----------
- fixed deletion/moving of messages from within "show" page
2006/11/09 (thomasb)
----------
- Little bugfix in HTML encoding
- Fixed encoding issues and delete-on-reply problem
- Corrected template parsing
2006/11/07 (estadtherr)
----------
- Upgraded to TinyMCE v2.0.8
- Fixed CSS path for editor popups
2006/09/26 (estadtherr)
----------
- Added spellchecker plugin to TinyMCE configuration
- Fixed HTML/Plain toggle labels
2006/09/24 (thomasb)
----------
- Partial client re-write with a common list class
- Re-enabled multi select of contacts (Bug #1484017)
- Enable contact editing right after creation (Bug #1459641)
- Updated Hungarian, Estonian and Traditional Chinese localization
2006/09/19 (thomasb)
----------
- Correct UTF-7 to UTF-8 conversion if mbstring is not available
2006/09/13 (estadtherr)
----------
- Introduction of TinyMCE HTML editor support for message composition and signatures
Note : a new column is added to the "identities" database table
2006/09/12 (estadtherr)
----------
- Fixed html2text treatment of table headers (Bug #1484020)
- Fixed IMAP fetch of message body (Bug #1484019)
2006/09/08 (thomasb)
----------
- Fixed safe_mode problems (Bug #1418381)
- Fixed wrong header encoding (Bug #1483976)
2006/09/07 (thomasb)
----------
- Made automatic draft saving configurable
- Fixed JS bug when renaming folders (Bug #1483989)
- Don't wait for complete page load when calling JavaScript init()
- Some improvements to prevent session expiration
- Prevent from double submit of spell check requests
2006/09/01 (thomasb)
----------
- Imporoved message parsing and HTML validation
- Added quota display as image (by Brett Patterson)
- Corrected creation of a message-id
- Updated Norwegian (bokmal) localization
2006/08/30 (thomasb)
----------
- New indentation for quoted message text
- Improved HTML validity
2006/08/28 (estadtherr)
----------
- Fixed URL character set (Ticket #1445501)
- Fixed saving of contact into MySQL from LDAP query results (Ticket #1483820)
2006/08/25 (thomasb)
----------
- Fixed folder renaming: unsubscribe before rename (Bug #1483920)
- Finalized new message parsing (+ chaching)
- Updated SQL scripts and UPGRADING instructions
2006/08/23 (thomasb)
----------
- Updated Polish, Portuguese, Latvian, Chinese and Japanese localization
2006/08/20 (thomasb)
----------
- Fixed wrong usage of mbstring (Bug #1462439)
- Set default spelling language (Ticket #1483938)
- Added support for Nox Spell Server
2006/08/18 (thomasb)
----------
- Re-built message parsing (Bug #1327068)
Now based on the message structure delivered by the IMAP server.
- Fixed some XSS and SQL injection issues
2006/08/10 (thomasb)
----------
- Fixed charset problems with folder renaming
2006/08/04 (thomasb)
----------
- Fixed Bug in saving identities (Ticket #1483915)
- Set folder name in window title (Bug #1483919)
- Don't add imap_root to INBOX path (Bug #1483816)
- Attempt to create default folders only after login
- Avoid usage of $CONFIG in rcube_imap class
2006/07/30 (thomasb)
----------
- Alter links in HTML messages (Bug #1326402)
- Added fallback if host not found in 'mail_domain' array
- Applied patch of Charles to highlight droptargets (Ticket #1473034)
- Fixed folder renaming (Bug #1483914)
- Added confirmation message after deleting a folder
2006/07/25 (thomasb)
----------
- Made folder renaming a bit more ajax-style
- Changed rename-labels and German translation
- Fixed addressbox countbar width (Bug #1483845)
- Fixed refresh interval problems in Safari (Bug #1483902)
- Fixed clear_message_list_header() errors (Bug #1483898)
- Sanity check of $message_set in imap.inc (Bug #1443200)
- Added correct changing of message list headers for Sent folder
- Updated Spanish localization (Ticket #1483887)
- Applied patch #1483846
2006/07/24 (richs)
----------
- Draft window no longer reloads. It saves to an iframe in the background instead (fixes bug #1483869)
- Draft timer now part of program/js/app.js instead of skins/default/templates/compose.inc
- Draft saving now properly returns an error when saving fails
- Draft timer stops and resets properly when attachments are uploaded, or when saving manually
- Old compose session/attachments are now cleaned up when a new/forward/reply/draft is made/opened
2006/07/19 (thomasb)
----------
- Correct entity encoding of link urls (HTML validity)
- Improved usability in compose step (Ticket #1483807)
- Added absolute URLs to several buttons (for "open in new window")
- Applied patch #1328032
- Fixed Bug/Patch #1443200
2006/07/18 (thomasb)
----------
- Fixed password with spaces issue (Bug #1364122)
- Replaced _auth hash with second cookie (Ticket #1483811)
- Don't use get_input_value() for passwords (Bug #1468895)
- Made password encryption key configurable
- Minor bugfixes with charset encoding
- Added <label> tags to forms (Ticket #1483810)
2006/07/07 (thomasb)
----------
- Fixed INSTALL_PATH bug #1425663
2006/07/03 (richs)
----------
- Fixed compatibility with in-body email addresses containing "+" (Bug #1483836)
- Updated French localizations (Ticket #1483862)
- Incoming messages can now be moved to Drafts, edited, saved, then moved back (Feature #1436191)
- Added Firefox workaround when clicking whitespace to drag messages (Bug #1483857)
- Corrected Dutch and Italian localizations (Ticket #1483851 and #1483848)
- Enabled 'Empty' (purge) command for Junk mailbox (defined in main.inc.php)
2006/06/30 (richs)
----------
- Fixed empty INBOX compatibility bug (Patch #1443200)
- Temporarily fixed French "compact" localization (Patch #1483862)
- Fixed "Select All" not working with Delete interface button (Bug #1332434)
- Fixed messsage list column compatibility with Konqueror (Bug #1395711)
- Fixed "unread count" in window title when count changed (Bug #1483812)
- Fixed DB error when deleting from message table (Patch #1483835)
2006/06/29 (richs)
----------
- Added ability to remove attachments (Feature #1436721)
- Default folders are now auto-created on first login (Feature #1471594)
- Fixed compatibility with folder apostrophes (e.g.: Joe's Folder) (Bug #1429458)
- Corrected Italian localizations
- Tweaked rename-folder form to clear after a rename
2006/06/26 (richs)
----------
- Added button to immediately check for new messages
- New message checking now displays status "Checking for new messages..."
- New message checking now looks for unread messages in all mailboxes (Feature #1326401)
- Task buttons now respond to clicks by darkening (as in other applications)
- Fixed "Sender" column changing to "Recipient" for "Sent" and "Drafts" message lists
- Added ability to sort messages by "Size"
- Added ability to rename folders (Feature #1326396)
- Added 'protect_default_folders' option to main.inc.php to prevent renames/deletes/unsubscribes of default folders
- Corrected 5 typos of "INSTLL" to "INSTALL" in program/include/main.inc
2006/06/25
----------
- Changed behavior to include host-specific configuration (Bug #1483849)
- Assume ISO-8859-1 encoding of mail messages by default (Patch #1483839)
- Fixed spell checker to work with the new URL at google.com
- Some memory and security optimizations sendmail.inc
- Updated UGRADING description
2006/06/19
----------
- Added Drafts support (Feature #1326839) (richs)
2006/06/02
----------
- Updated Estonian localization and moved from ee to et
- Added Bulgarian localization
2006/05/25
----------
- Finalized GoogieSpell integration
2006/05/18
----------
- Added Arabic and Armenian localizations
- Updated Russian localization
- Removed MDB2 classes from repository. Install them seperately if used.
- Updated MDB2 wrapper class contributed by Lukas Kahwe Smith
- Allow & in e-mail addresses
2006/05/05
----------
- Fixed typos in function rcube_button() (Bugs #1473198 and #1473201)
- Check for zlib.output_compression before using ob_gzhandler (Bug #1471069)
- Casting date parts in iil_StrToTime() to avoid warnings (Bug #1482140)
- Corrected INSTALL description (Bug #1476106)
- Added charset to javascript HTTP headers
- Fixed Opera bug with CC and BCC fields (Bug #1474576)
- Changed login page title regarding product name (Bug #1476413)
- Pimped search function
- Applied attachment viewing/forwarding patches by Andrew Fladmark
- Applied prev/next patch by Leonard Bouchet
- Applied patches by Mark Bucciarelli
- Applied patch for requesting receipts by Salvatore Ansani
- Integrated GoogieSpell as suggested by phil (styling is not perfect yet, localization is missing)
2006/04/13
----------
- Added Slovenian localization
- Updated Portuguese localization
- Fixed parent.location problem for compose-links
- Added sort order saving patch by Jacob Brunson
- Added gzip compression support
2006/04/02
----------
- Added Lithuanian localization
- Improved search function
- Added version string as template object
- Load host-specific configuration file (see config/main.inc.php)
- New config parameter adding domain to user names for login
- Strip tags on _auth, _action, _task parameters
- Corrected labels for next/previous page buttons in address book
2006/03/23
----------
- Auto-detect mail header delimiters
- Regard daylight savings
- Localized quota display
- Started implementing search function
2006/03/20
----------
- Avoid error message when saving an unchanged identity (Bug #1429510)
- Fixed hard-coded cols selection for sent folder (Bug #1354586)
- Enable some HTML links for use with "open in new window" or "save target"
- Check meta-key instead of ctrl on Macs
- Ignore double clicks when holding down a modifier key
- Fixed reloading of the login page
- Fixed typo in compose template (Bug #1446852)
- Added compose button to message read step (Request #1433288)
- New config parameter for persistent database connections (Bug #1431817)
2006/03/14
----------
- Don't remove internal HTML tags in plaintext messages
- Improved error handling in DB connection failure
2006/02/22
----------
- Updated localizations
- Fixed bug #1435989
2006/02/19
----------
- Updated localizations
- Applied patch of Anders Karlsson
- Applied patch of Jacob Brunson
- Applied patch for virtuser_query by Robin Elfrink
- Added support for References header (patch by Auke)
- Added support for mbstring module by Tadashi Jokagi
- Added function for automatic remove of slashes on GET and POST vars
if magic_quotes is enabled
2006/02/05
----------
- Added Slovak, Hungarian, Bosnian and Croation translation
- Fixed bug when inserting signatures with !?&
- Chopping message headers before inserting into the message cache table
(to avoid bugs in Postgres)
- Allow one-char domains in e-mail addresses
- Make product name in page title configurable
- Make username available as skin object
- Added session_write_close() in rcube_db class destructor to avoid problems
in PHP 5.0.5
- Use move_uploaded_file() instead of copy() for a more secure handling of
uploaded attachments
- Additional config parameter to show/hide deleted messages
- Added periodic request for checking new mails (Request #1307821)
- Added EXPUNGE command
- Optimized loading time for mail interface
- Changed to full UTF-8 support
- Additional timezones (Patch #1389912)
- Added LDAP public search (experimental)
- Applied patch for correct ctrl/shift behavior for message selection (Bug #1326364)
- Casting to strings when adding empty headers to message cache (Bug #1406026)
- Skip sender-address as recipient when Reply-to-all
- Fixes in utf8-class
- Added patch for Quota display by Aury Fink Filho <nuny@aury.com.br>
- Added garbage collector for message cache
- Added patches for BCC headers
2005/12/16
----------
- Added Turkish and Simplified Chinese translation
- Use virtusertable to resolve e-mail addresses at login
- Configurable mail_domain used to compose correct e-mail addresses
on first login
2005/12/03
----------
- Added Finnish, Romanian, Polish, Czech, British, Norwegian, Greek, Russian,
Estonian and Chinese translation
- Get IMAP server capabilities in array
- Check for NAMESPACE capability before sending command
- Set default user language from config 'locale_string'
- Added sorting patch for message list
- Make default sort col/order configurable
- Fixed XSS in address book and identities
- Added more XSS protection (Bug #1308236)
- Added tab indexes for compose form
- Added 'changed' col to contacts table
- Support for 160-bit session hashes
- Added input check for contacts and identities (Patch #1346523)
- Added messages/warning to compose step (Patch #1323895)
- Added favicon to the default skin
- Fixed Bug #1334337 as far as possible
- Added Reply-To-All functionality (Request #1326395, Patch #1349777)
- Redesign of client side AJAX code (enable multi threading)
- Added keep-alive signal every minute
- Make logs dir configurable
- Added support for SMTPS
- Decode attachment file names
- Make delimiter for message headers configurable
- Add generic footer to sent messages
- Choose the rigt identity when replying
- Remove signature when replying (Request #1333167)
- Signatures for each identity
- Select charset when composing message
- Complete re-design of the caching mechanism
2005/08/11
----------
- Write list header to client even if list is empty
- Add functions "select all", "select none" to message list
- Improved filter for HTML messages to remove potentially malicious tags
(script, iframe, object) and event handlers.
- Buttons for next/previous message in view mode
- Add new created contact to list and show confirmation status
- Added folder management (subscribe/create/delete)
- Log message sending (SMTP log)
- Grant access for Camino browser
- Added German translation
2005/10/20
----------
- Added Swedish, Latvian, Portuguese and Catalan translation
- Make SMTP auth method configurable
- Make mailboxlist scrollable (Bug #1326372)
- Fixed SSL support
- Improved support for Courier IMAP (root folder and delimiter issues)
- Moved taskbar from bottom to top
- Added 'session_lifetime' parameter
- Fixed wrong unread count when deleting message (Bug #1332434)
- Srip tags when creating a new folder (Bug #1332084)
- Translate HTML tags in message headers (Bug #1330134)
- Correction in German translation (Bug #1329434)
- Display folder names with special chars correctly (Bug #1330157)
2005/10/07
----------
- Added French, Italian, Spanish, Danish, Dutch translation
- Clarified license (Bug #1305966)
- Fixed PHP warnings (Bug #1299403)
- Fixed english translation (Bug #1295406)
- Fixed bug #1290833: Last character of email not seen
- Fixed bug #1292199 when creating new user
- Allow more browsers (Bug #1285101)
- Added setting for showing pretty dates
- Added support for SQLite database
- Make use of message caching configurable
- Also add attachments when forwarding a message
- Long folder names will not flow over message list (Bug #1267232)
- Show nested mailboxes hieracically
- Enable IMAPS by host
2005/08/20
----------
- Improved cacheing of mailbox messagecount
- Fixed javascript bug when creating a new message folder
- Fixed javascript bugs #1260990 and #1260992: folder selection
- Make Trash folder configurable
- Auto create folders Inbox, Sent and Trash (if configured)
- Support for IMAP root folder
- Added support fot text/enriched messages
- Make list of special mailboxes configurable
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index ec257b69b..606c977a0 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1,1503 +1,1507 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/mail/func.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Provide webmail functionality and GUI objects |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
require_once('lib/html2text.inc');
require_once('lib/enriched.inc');
$EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
if (empty($_SESSION['mbox'])){
$_SESSION['mbox'] = $IMAP->get_mailbox_name();
}
// set imap properties and session vars
if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
{
$IMAP->set_mailbox($mbox);
$_SESSION['mbox'] = $mbox;
}
if (!empty($_GET['_page']))
{
$IMAP->set_page((int)$_GET['_page']);
$_SESSION['page'] = (int)$_GET['_page'];
}
// set mailbox to INBOX if not set
if (empty($_SESSION['mbox']))
$_SESSION['mbox'] = $IMAP->get_mailbox_name();
// set default sort col/order to session
if (!isset($_SESSION['sort_col']))
$_SESSION['sort_col'] = $CONFIG['message_sort_col'];
if (!isset($_SESSION['sort_order']))
$_SESSION['sort_order'] = $CONFIG['message_sort_order'];
// set message set for search result
if (!empty($_GET['_search']) && isset($_SESSION['search'][$_GET['_search']]))
$IMAP->set_search_set($_SESSION['search'][$_GET['_search']]);
// define url for getting message parts
if (strlen($_GET['_uid']))
$GET_URL = sprintf('%s&_action=get&_mbox=%s&_uid=%d', $COMM_PATH, $IMAP->get_mailbox_name(), get_input_value('_uid', RCUBE_INPUT_GET));
// set current mailbox in client environment
$OUTPUT->add_script(sprintf("%s.set_env('mailbox', '%s');", $JS_OBJECT_NAME, $IMAP->get_mailbox_name()));
if ($CONFIG['trash_mbox'])
$OUTPUT->add_script(sprintf("%s.set_env('trash_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['trash_mbox']));
if ($CONFIG['drafts_mbox'])
$OUTPUT->add_script(sprintf("%s.set_env('drafts_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['drafts_mbox']));
if ($CONFIG['junk_mbox'])
$OUTPUT->add_script(sprintf("%s.set_env('junk_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['junk_mbox']));
// return the mailboxlist in HTML
function rcmail_mailbox_list($attrib)
{
global $IMAP, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $COMM_PATH;
static $s_added_script = FALSE;
static $a_mailboxes;
// add some labels to client
rcube_add_label('purgefolderconfirm');
rcube_add_label('deletemessagesconfirm');
// $mboxlist_start = rcube_timer();
$type = $attrib['type'] ? $attrib['type'] : 'ul';
$add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
array('style', 'class', 'id');
if ($type=='ul' && !$attrib['id'])
$attrib['id'] = 'rcmboxlist';
// allow the following attributes to be added to the <ul> tag
$attrib_str = create_attrib_string($attrib, $add_attrib);
$out = '<' . $type . $attrib_str . ">\n";
// add no-selection option
if ($type=='select' && $attrib['noselection'])
$out .= sprintf('<option value="0">%s</option>'."\n",
rcube_label($attrib['noselection']));
// get mailbox list
$mbox_name = $IMAP->get_mailbox_name();
// for these mailboxes we have localized labels
$special_mailboxes = array('inbox', 'sent', 'drafts', 'trash', 'junk');
// build the folders tree
if (empty($a_mailboxes))
{
// get mailbox list
$a_folders = $IMAP->list_mailboxes();
$delimiter = $IMAP->get_hierarchy_delimiter();
$a_mailboxes = array();
// rcube_print_time($mboxlist_start, 'list_mailboxes()');
foreach ($a_folders as $folder)
rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
}
// var_dump($a_mailboxes);
if ($type=='select')
$out .= rcmail_render_folder_tree_select($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
else
$out .= rcmail_render_folder_tree_html($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
// rcube_print_time($mboxlist_start, 'render_folder_tree()');
if ($type=='ul')
$OUTPUT->add_script(sprintf("%s.gui_object('mailboxlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
return $out . "</$type>";
}
// create a hierarchical array of the mailbox list
function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
{
$pos = strpos($folder, $delm);
if ($pos !== false)
{
$subFolders = substr($folder, $pos+1);
$currentFolder = substr($folder, 0, $pos);
}
else
{
$subFolders = false;
$currentFolder = $folder;
}
$path .= $currentFolder;
if (!isset($arrFolders[$currentFolder]))
{
$arrFolders[$currentFolder] = array('id' => $path,
'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
'folders' => array());
}
if (!empty($subFolders))
rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
}
// return html for a structured list <ul> for the mailbox tree
function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
{
global $JS_OBJECT_NAME, $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
$idx = 0;
$out = '';
foreach ($arrFolders as $key => $folder)
{
$zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
$title = '';
$folder_lc = strtolower($folder['id']);
if (in_array($folder_lc, $special))
$foldername = rcube_label($folder_lc);
else
{
$foldername = $folder['name'];
// shorten the folder name to a given length
if ($maxlength && $maxlength>1)
{
$fname = abbrevate_string($foldername, $maxlength);
if ($fname != $foldername)
$title = ' title="'.Q($foldername).'"';
$foldername = $fname;
}
}
// add unread message count display
if ($unread_count = $IMAP->messagecount($folder['id'], 'RECENT', ($folder['id']==$mbox_name)))
$foldername .= sprintf(' (%d)', $unread_count);
// make folder name safe for ids and class names
$folder_css = $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_lc);
// set special class for Sent, Drafts, Trash and Junk
if ($folder['id']==$CONFIG['sent_mbox'])
$class_name = 'sent';
else if ($folder['id']==$CONFIG['drafts_mbox'])
$class_name = 'drafts';
else if ($folder['id']==$CONFIG['trash_mbox'])
$class_name = 'trash';
else if ($folder['id']==$CONFIG['junk_mbox'])
$class_name = 'junk';
$js_name = htmlspecialchars(JQ($folder['id']));
$out .= sprintf('<li id="rcmbx%s" class="mailbox %s %s%s%s"><a href="%s&amp;_mbox=%s"'.
' onclick="return %s.command(\'list\',\'%s\')"'.
' onmouseover="return %s.focus_mailbox(\'%s\')"' .
' onmouseout="return %s.unfocus_mailbox(\'%s\')"' .
' onmouseup="return %s.mbox_mouse_up(\'%s\')"%s>%s</a>',
$folder_css,
$class_name,
$zebra_class,
$unread_count ? ' unread' : '',
$folder['id']==$mbox_name ? ' selected' : '',
$COMM_PATH,
urlencode($folder['id']),
$JS_OBJECT_NAME,
$js_name,
$JS_OBJECT_NAME,
$js_name,
$JS_OBJECT_NAME,
$js_name,
$JS_OBJECT_NAME,
$js_name,
$title,
Q($foldername));
if (!empty($folder['folders']))
$out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
$out .= "</li>\n";
$idx++;
}
return $out;
}
// return html for a flat list <select> for the mailbox tree
function rcmail_render_folder_tree_select(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
{
global $IMAP, $OUTPUT;
$idx = 0;
$out = '';
foreach ($arrFolders as $key=>$folder)
{
$folder_lc = strtolower($folder['id']);
if (in_array($folder_lc, $special))
$foldername = rcube_label($folder_lc);
else
{
$foldername = $folder['name'];
// shorten the folder name to a given length
if ($maxlength && $maxlength>1)
$foldername = abbrevate_string($foldername, $maxlength);
}
$out .= sprintf('<option value="%s">%s%s</option>'."\n",
htmlspecialchars($folder['id']),
str_repeat('&nbsp;', $nestLevel*4),
Q($foldername));
if (!empty($folder['folders']))
$out .= rcmail_render_folder_tree_select($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1);
$idx++;
}
return $out;
}
// return the message list as HTML table
function rcmail_message_list($attrib)
{
global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT, $JS_OBJECT_NAME;
$skin_path = $CONFIG['skin_path'];
$image_tag = '<img src="%s%s" alt="%s" border="0" />';
// check to see if we have some settings for sorting
$sort_col = $_SESSION['sort_col'];
$sort_order = $_SESSION['sort_order'];
// add some labels to client
rcube_add_label('from', 'to');
// get message headers
$a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcubemessagelist';
// allow the following attributes to be added to the <table> tag
$attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
$out = '<table' . $attrib_str . ">\n";
// define list of cols to be displayed
$a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
$a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
// show 'to' instead of from in sent messages
if (($IMAP->get_mailbox_name()==$CONFIG['sent_mbox'] || $IMAP->get_mailbox_name()==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
&& !array_search('to', $a_show_cols))
$a_show_cols[$f] = 'to';
// add col definition
$out .= '<colgroup>';
$out .= '<col class="icon" />';
foreach ($a_show_cols as $col)
$out .= sprintf('<col class="%s" />', $col);
$out .= '<col class="icon" />';
$out .= "</colgroup>\n";
// add table title
$out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
$javascript = '';
foreach ($a_show_cols as $col)
{
// get column name
$col_name = Q(rcube_label($col));
// make sort links
$sort = '';
if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
{
// have buttons configured
if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
{
$sort = '&nbsp;&nbsp;';
// asc link
if (!empty($attrib['sortascbutton']))
{
$sort .= rcube_button(array('command' => 'sort',
'prop' => $col.'_ASC',
'image' => $attrib['sortascbutton'],
'align' => 'absmiddle',
'title' => 'sortasc'));
}
// desc link
if (!empty($attrib['sortdescbutton']))
{
$sort .= rcube_button(array('command' => 'sort',
'prop' => $col.'_DESC',
'image' => $attrib['sortdescbutton'],
'align' => 'absmiddle',
'title' => 'sortdesc'));
}
}
// just add a link tag to the header
else
{
$col_name = sprintf('<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
$JS_OBJECT_NAME,
$col,
rcube_label('sortby'),
$col_name);
}
}
$sort_class = $col==$sort_col ? " sorted$sort_order" : '';
// put it all together
$out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";
}
$out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
$out .= "</tr></thead>\n<tbody>\n";
// no messages in this mailbox
if (!sizeof($a_headers))
{
$out .= sprintf('<tr><td colspan="%d">%s</td></tr>',
sizeof($a_show_cols)+2,
Q(rcube_label('nomessagesfound')));
}
$a_js_message_arr = array();
// create row for each message
foreach ($a_headers as $i => $header) //while (list($i, $header) = each($a_headers))
{
$message_icon = $attach_icon = '';
$js_row_arr = array();
$zebra_class = $i%2 ? 'even' : 'odd';
// set messag attributes to javascript array
if ($header->deleted)
$js_row_arr['deleted'] = true;
if (!$header->seen)
$js_row_arr['unread'] = true;
if ($header->answered)
$js_row_arr['replied'] = true;
// set message icon
if ($attrib['deletedicon'] && $header->deleted)
$message_icon = $attrib['deletedicon'];
else if ($attrib['unreadicon'] && !$header->seen)
$message_icon = $attrib['unreadicon'];
else if ($attrib['repliedicon'] && $header->answered)
$message_icon = $attrib['repliedicon'];
else if ($attrib['messageicon'])
$message_icon = $attrib['messageicon'];
// set attachment icon
if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
$attach_icon = $attrib['attachmenticon'];
$out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
$header->uid,
$header->seen ? '' : ' unread',
$header->deleted ? ' deleted' : '',
$zebra_class);
$out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
// format each col
foreach ($a_show_cols as $col)
{
if ($col=='from' || $col=='to')
$cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
else if ($col=='subject')
{
$cont = Q($IMAP->decode_header($header->$col));
+ if (!$cont) $cont = Q(rcube_label('nosubject'));
// firefox/mozilla temporary workaround to pad subject with content so that whitespace in rows responds to drag+drop
$cont .= '<img src="./program/blank.gif" height="5" width="1000" alt="" />';
}
else if ($col=='size')
$cont = show_bytes($header->$col);
else if ($col=='date')
$cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date));
else
$cont = Q($header->$col);
$out .= '<td class="'.$col.'">' . $cont . "</td>\n";
}
$out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
$out .= "</tr>\n";
if (sizeof($js_row_arr))
$a_js_message_arr[$header->uid] = $js_row_arr;
}
// complete message table
$out .= "</tbody></table>\n";
$message_count = $IMAP->messagecount();
// set client env
$javascript .= sprintf("%s.gui_object('mailcontframe', '%s');\n", $JS_OBJECT_NAME, 'mailcontframe');
$javascript .= sprintf("%s.gui_object('messagelist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
$javascript .= sprintf("%s.set_env('messagecount', %d);\n", $JS_OBJECT_NAME, $message_count);
$javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $IMAP->list_page);
$javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($message_count/$IMAP->page_size));
$javascript .= sprintf("%s.set_env('sort_col', '%s');\n", $JS_OBJECT_NAME, $sort_col);
$javascript .= sprintf("%s.set_env('sort_order', '%s');\n", $JS_OBJECT_NAME, $sort_order);
if ($attrib['messageicon'])
$javascript .= sprintf("%s.set_env('messageicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['messageicon']);
if ($attrib['deletedicon'])
$javascript .= sprintf("%s.set_env('deletedicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['deletedicon']);
if ($attrib['unreadicon'])
$javascript .= sprintf("%s.set_env('unreadicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['unreadicon']);
if ($attrib['repliedicon'])
$javascript .= sprintf("%s.set_env('repliedicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['repliedicon']);
if ($attrib['attachmenticon'])
$javascript .= sprintf("%s.set_env('attachmenticon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['attachmenticon']);
$javascript .= sprintf("%s.set_env('messages', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr));
$OUTPUT->add_script($javascript);
$OUTPUT->include_script('list.js');
return $out;
}
// return javascript commands to add rows to the message list
function rcmail_js_message_list($a_headers, $insert_top=FALSE)
{
global $CONFIG, $IMAP;
$commands = '';
$a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
// show 'to' instead of from in sent messages
if (($IMAP->get_mailbox_name()==$CONFIG['sent_mbox'] || $IMAP->get_mailbox_name()==$CONFIG['drafts_mbox'])
&& ($f = array_search('from', $a_show_cols)) && !array_search('to', $a_show_cols))
$a_show_cols[$f] = 'to';
$commands .= sprintf("this.set_message_coltypes(%s);\n", array2js($a_show_cols));
// loop through message headers
for ($n=0; $a_headers[$n]; $n++)
{
$header = $a_headers[$n];
$a_msg_cols = array();
$a_msg_flags = array();
// format each col; similar as in rcmail_message_list()
foreach ($a_show_cols as $col)
{
if ($col=='from' || $col=='to')
$cont = Q(rcmail_address_string($header->$col, 3), 'show');
else if ($col=='subject')
+ {
$cont = Q($IMAP->decode_header($header->$col));
+ if (!$cont) $cont = Q(rcube_label('nosubject'));
+ }
else if ($col=='size')
$cont = show_bytes($header->$col);
else if ($col=='date')
$cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date));
else
$cont = Q($header->$col);
$a_msg_cols[$col] = $cont;
}
$a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
$a_msg_flags['unread'] = $header->seen ? 0 : 1;
$a_msg_flags['replied'] = $header->answered ? 1 : 0;
$commands .= sprintf("this.add_message_row(%s, %s, %s, %b, %b);\n",
$header->uid,
array2js($a_msg_cols),
array2js($a_msg_flags),
preg_match("/multipart\/m/i", $header->ctype),
$insert_top);
}
return $commands;
}
// return an HTML iframe for loading mail content
function rcmail_messagecontent_frame($attrib)
{
global $OUTPUT, $JS_OBJECT_NAME;
if (empty($attrib['id']))
$attrib['id'] = 'rcmailcontentwindow';
// allow the following attributes to be added to the <iframe> tag
$attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
$framename = $attrib['id'];
$out = sprintf('<iframe name="%s"%s></iframe>'."\n",
$framename,
$attrib_str);
$OUTPUT->add_script("$JS_OBJECT_NAME.set_env('contentframe', '$framename');");
return $out;
}
// return code for search function
function rcmail_search_form($attrib)
{
global $OUTPUT, $JS_OBJECT_NAME;
// add some labels to client
rcube_add_label('searching');
$attrib['name'] = '_q';
if (empty($attrib['id']))
$attrib['id'] = 'rcmqsearchbox';
$input_q = new textfield($attrib);
$out = $input_q->show();
$OUTPUT->add_script(sprintf("%s.gui_object('qsearchbox', '%s');",
$JS_OBJECT_NAME,
$attrib['id']));
// add form tag around text field
if (empty($attrib['form']))
$out = sprintf('<form name="rcmqsearchform" action="./" '.
'onsubmit="%s.command(\'search\');return false" style="display:inline;">%s</form>',
$JS_OBJECT_NAME,
$out);
return $out;
}
function rcmail_messagecount_display($attrib)
{
global $IMAP, $OUTPUT, $JS_OBJECT_NAME;
if (!$attrib['id'])
$attrib['id'] = 'rcmcountdisplay';
$OUTPUT->add_script(sprintf("%s.gui_object('countdisplay', '%s');",
$JS_OBJECT_NAME,
$attrib['id']));
// allow the following attributes to be added to the <span> tag
$attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
$out = '<span' . $attrib_str . '>';
$out .= rcmail_get_messagecount_text();
$out .= '</span>';
return $out;
}
function rcmail_quota_display($attrib)
{
global $OUTPUT, $JS_OBJECT_NAME, $COMM_PATH;
if (!$attrib['id'])
$attrib['id'] = 'rcmquotadisplay';
$OUTPUT->add_script(sprintf("%s.gui_object('quotadisplay', '%s');", $JS_OBJECT_NAME, $attrib['id']));
// allow the following attributes to be added to the <span> tag
$attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
$out = '<span' . $attrib_str . '>';
$out .= rcmail_quota_content($attrib['display']);
$out .= '</span>';
return $out;
}
function rcmail_quota_content($display)
{
global $IMAP, $COMM_PATH;
if (!$IMAP->get_capability('QUOTA'))
$quota_text = rcube_label('unknown');
else if ($quota = $IMAP->get_quota())
{
$quota_text = sprintf("%s / %s (%.0f%%)",
show_bytes($quota["used"] * 1024),
show_bytes($quota["total"] * 1024),
$quota["percent"]);
// show quota as image (by Brett Patterson)
if ($display == 'image' && function_exists('imagegif'))
{
$attrib = array('width' => 100, 'height' => 14);
$quota_text = sprintf('<img src="%s&amp;_action=quotaimg&amp;u=%s&amp;q=%d&amp;w=%d&amp;h=%d" width="%d" height="%d" alt="%s" title="%s / %s" />',
$COMM_PATH,
$quota['used'], $quota['total'],
$attrib['width'], $attrib['height'],
$attrib['width'], $attrib['height'],
$quota_text,
show_bytes($quota["used"] * 1024),
show_bytes($quota["total"] * 1024));
}
}
else
$quota_text = rcube_label('unlimited');
return $quota_text;
}
function rcmail_get_messagecount_text($count=NULL, $page=NULL)
{
global $IMAP, $MESSAGE;
if (isset($MESSAGE['index']))
{
return rcube_label(array('name' => 'messagenrof',
'vars' => array('nr' => $MESSAGE['index']+1,
'count' => $count!==NULL ? $count : $IMAP->messagecount())));
}
if ($page===NULL)
$page = $IMAP->list_page;
$start_msg = ($page-1) * $IMAP->page_size + 1;
$max = $count!==NULL ? $count : $IMAP->messagecount();
if ($max==0)
$out = rcube_label('mailboxempty');
else
$out = rcube_label(array('name' => 'messagesfromto',
'vars' => array('from' => $start_msg,
'to' => min($max, $start_msg + $IMAP->page_size - 1),
'count' => $max)));
return Q($out);
}
function rcmail_print_body($part, $safe=FALSE, $plain=FALSE)
{
global $IMAP, $REMOTE_OBJECTS, $JS_OBJECT_NAME;
$body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
// text/html
if ($part->ctype_secondary=='html')
{
// remove charset specification in HTML message
$body = preg_replace('/charset=[a-z0-9\-]+/i', '', $body);
if (!$safe) // remove remote images and scripts
{
$remote_patterns = array('/<img\s+(.*)src=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
'/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
'/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
'/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
'/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
'/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
'/<script.+<\/script>/Umis');
$remote_replaces = array('<img \\1src=\\2./program/blocked.gif\\4',
'',
'',
'',
'none',
'none',
'');
// set flag if message containes remote obejcts that where blocked
foreach ($remote_patterns as $pattern)
{
if (preg_match($pattern, $body))
{
$REMOTE_OBJECTS = TRUE;
break;
}
}
$body = preg_replace($remote_patterns, $remote_replaces, $body);
}
return Q($body, 'show', FALSE);
}
// text/enriched
if ($part->ctype_secondary=='enriched')
{
return Q(enriched_to_html($body), 'show');
}
else
{
// make links and email-addresses clickable
$convert_patterns = $convert_replaces = $replace_strings = array();
$url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
$url_chars_within = '\?\.~,!';
$convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
$convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
$convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
$convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
$convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
$convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return $JS_OBJECT_NAME.command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
if ($part->ctype_parameters['format'] != 'flowed')
$body = wordwrap(trim($body), 80);
$body = preg_replace($convert_patterns, $convert_replaces, $body);
// split body into single lines
$a_lines = preg_split('/\r?\n/', $body);
$quote_level = 0;
// colorize quoted parts
for($n=0; $n<sizeof($a_lines); $n++)
{
$line = $a_lines[$n];
$quotation = '';
$q = 0;
if (preg_match('/^(>+\s*)/', $line, $regs))
{
$q = strlen(preg_replace('/\s/', '', $regs[1]));
$line = substr($line, strlen($regs[1]));
if ($q > $quote_level)
$quotation = str_repeat('<blockquote>', $q - $quote_level);
else if ($q < $quote_level)
$quotation = str_repeat("</blockquote>", $quote_level - $q);
}
else if ($quote_level > 0)
$quotation = str_repeat("</blockquote>", $quote_level);
$quote_level = $q;
$a_lines[$n] = $quotation . Q($line, 'replace', FALSE);
}
// insert the links for urls and mailtos
$body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
return "<div class=\"pre\">".$body."\n</div>";
}
}
// add a string to the replacement array and return a replacement string
function rcmail_str_replacement($str, &$rep)
{
static $count = 0;
$rep[$count] = stripslashes($str);
return "##string_replacement{".($count++)."}##";
}
function rcmail_parse_message(&$structure, $arg=array(), $recursive=FALSE)
{
global $IMAP;
static $sa_inline_objects = array();
// arguments are: (bool)$prefer_html, (string)$get_url
extract($arg);
$a_attachments = array();
$a_return_parts = array();
$out = '';
$message_ctype_primary = strtolower($structure->ctype_primary);
$message_ctype_secondary = strtolower($structure->ctype_secondary);
// show message headers
if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
{
$c = new stdClass;
$c->type = 'headers';
$c->headers = &$structure->headers;
$a_return_parts[] = $c;
}
// print body if message doesn't have multiple parts
if ($message_ctype_primary=='text')
{
$structure->type = 'content';
$a_return_parts[] = &$structure;
}
// message contains alternative parts
else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
{
// get html/plaintext parts
$plain_part = $html_part = $print_part = $related_part = NULL;
foreach ($structure->parts as $p => $sub_part)
{
$sub_ctype_primary = strtolower($sub_part->ctype_primary);
$sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
// check if sub part is
if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
$plain_part = $p;
else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
$html_part = $p;
else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
$enriched_part = $p;
else if ($sub_ctype_primary=='multipart' && $sub_ctype_secondary=='related')
$related_part = $p;
}
// parse related part (alternative part could be in here)
if ($related_part!==NULL && $prefer_html)
{
list($parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
$a_return_parts = array_merge($a_return_parts, $parts);
$a_attachments = array_merge($a_attachments, $attachmnts);
}
// print html/plain part
else if ($html_part!==NULL && $prefer_html)
$print_part = &$structure->parts[$html_part];
else if ($enriched_part!==NULL)
$print_part = &$structure->parts[$enriched_part];
else if ($plain_part!==NULL)
$print_part = &$structure->parts[$plain_part];
// show message body
if (is_object($print_part))
{
$print_part->type = 'content';
$a_return_parts[] = $print_part;
}
// show plaintext warning
else if ($html_part!==NULL)
{
$c = new stdClass;
$c->type = 'content';
$c->body = rcube_label('htmlmessage');
$c->ctype_primary = 'text';
$c->ctype_secondary = 'plain';
$a_return_parts[] = $c;
}
// add html part as attachment
if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
{
$html_part = &$structure->parts[$html_part];
$html_part->filename = rcube_label('htmlmessage');
$html_part->mimetype = 'text/html';
$a_attachments[] = $html_part;
}
}
// message contains multiple parts
else if (is_array($structure->parts) && !empty($structure->parts))
{
for ($i=0; $i<count($structure->parts); $i++)
{
$mail_part = &$structure->parts[$i];
$primary_type = strtolower($mail_part->ctype_primary);
$secondary_type = strtolower($mail_part->ctype_secondary);
// multipart/alternative
if ($primary_type=='multipart')
{
list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
$a_return_parts = array_merge($a_return_parts, $parts);
$a_attachments = array_merge($a_attachments, $attachmnts);
}
// part text/[plain|html] OR message/delivery-status
else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
($primary_type=='message' && $secondary_type=='delivery-status'))
{
$mail_part->type = 'content';
$a_return_parts[] = $mail_part;
}
// part message/*
else if ($primary_type=='message')
{
list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
$a_return_parts = array_merge($a_return_parts, $parts);
$a_attachments = array_merge($a_attachments, $attachmnts);
}
// part is file/attachment
else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
(empty($mail_part->disposition) && ($mail_part->d_parameters['filename'] || $mail_part->ctype_parameters['name'])))
{
// skip apple resource forks
if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile')
continue;
// part belongs to a related message
if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
{
$mail_part->filename = rcube_imap::decode_mime_string($mail_part->d_parameters['filename']);
$mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
$sa_inline_objects[] = $mail_part;
}
// is regular attachment
else if (($fname = $mail_part->d_parameters['filename']) ||
($fname = $mail_part->ctype_parameters['name']) ||
($fname = $mail_part->headers['content-description']))
{
$mail_part->filename = rcube_imap::decode_mime_string($fname);
$a_attachments[] = $mail_part;
}
}
}
// if this was a related part try to resolve references
if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
{
$a_replaces = array();
foreach ($sa_inline_objects as $inline_object)
$a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($get_url, $inline_object->mime_id));
// add replace array to each content part
// (will be applied later when part body is available)
for ($i=0; $i<count($a_return_parts); $i++)
{
if ($a_return_parts[$i]->type=='content')
$a_return_parts[$i]->replaces = $a_replaces;
}
}
}
// message is single part non-text
else
{
if (($fname = $structure->d_parameters['filename']) ||
($fname = $structure->ctype_parameters['name']) ||
($fname = $structure->headers['content-description']))
{
$structure->filename = rcube_imap::decode_mime_string($fname);
$a_attachments[] = $structure;
}
}
return array($a_return_parts, $a_attachments);
}
// return table with message headers
function rcmail_message_headers($attrib, $headers=NULL)
{
global $IMAP, $OUTPUT, $MESSAGE;
static $sa_attrib;
// keep header table attrib
if (is_array($attrib) && !$sa_attrib)
$sa_attrib = $attrib;
else if (!is_array($attrib) && is_array($sa_attrib))
$attrib = $sa_attrib;
if (!isset($MESSAGE))
return FALSE;
// get associative array of headers object
if (!$headers)
$headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
$header_count = 0;
// allow the following attributes to be added to the <table> tag
$attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
$out = '<table' . $attrib_str . ">\n";
// show these headers
$standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
foreach ($standard_headers as $hkey)
{
if (!$headers[$hkey])
continue;
if ($hkey=='date' && !empty($headers[$hkey]))
$header_value = format_date(strtotime($headers[$hkey]));
else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
$header_value = Q(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']), 'show');
else
$header_value = Q($IMAP->decode_header($headers[$hkey]));
$out .= "\n<tr>\n";
$out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
$out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
$header_count++;
}
$out .= "\n</table>\n\n";
return $header_count ? $out : '';
}
function rcmail_message_body($attrib)
{
global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS, $JS_OBJECT_NAME;
if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
return '';
if (!$attrib['id'])
$attrib['id'] = 'rcmailMsgBody';
$safe_mode = (bool)$_GET['_safe'];
$attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
$out = '<div '. $attrib_str . ">\n";
$header_attrib = array();
foreach ($attrib as $attr => $value)
if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
$header_attrib[$regs[1]] = $value;
// this is an ecrypted message
// -> create a plaintext body with the according message
if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
{
$p = new stdClass;
$p->type = 'content';
$p->ctype_primary = 'text';
$p->ctype_secondary = 'plain';
$p->body = rcube_label('encryptedmessage');
$MESSAGE['parts'][0] = $p;
}
if ($MESSAGE['parts'])
{
foreach ($MESSAGE['parts'] as $i => $part)
{
if ($part->type=='headers')
$out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
else if ($part->type=='content')
{
if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
$part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
// fetch part if not available
if (!isset($part->body))
$part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
$body = rcmail_print_body($part, $safe_mode);
$out .= '<div class="message-part">';
if ($part->ctype_secondary != 'plain')
$out .= rcmail_mod_html_body($body, $attrib['id']);
else
$out .= $body;
$out .= "</div>\n";
}
}
}
else
$out .= $MESSAGE['body'];
$ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
$ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
// list images after mail body
if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' && $ctype_secondary=='mixed' &&
sizeof($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
{
foreach ($MESSAGE['attachments'] as $attach_prop)
{
if (strpos($attach_prop->mimetype, 'image/')===0)
$out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
htmlspecialchars($GET_URL), $attach_prop->mime_id,
$attach_prop->filename,
$attach_prop->filename);
}
}
// tell client that there are blocked remote objects
if ($REMOTE_OBJECTS && !$safe_mode)
$OUTPUT->add_script(sprintf("%s.set_env('blockedobjects', true);", $JS_OBJECT_NAME));
$out .= "\n</div>";
return $out;
}
// modify a HTML message that it can be displayed inside a HTML page
function rcmail_mod_html_body($body, $container_id)
{
// remove any null-byte characters before parsing
$body = preg_replace('/\x00/', '', $body);
$last_style_pos = 0;
$body_lc = strtolower($body);
// find STYLE tags
while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
{
$pos = strpos($body_lc, '>', $pos)+1;
// replace all css definitions with #container [def]
$styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id);
$body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
$body_lc = strtolower($body);
$last_style_pos = $pos2;
}
// remove SCRIPT tags
foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
{
while (($pos = strpos($body_lc, '<'.$tag)) && ($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)))
{
$pos2 += strlen('</'.$tag.'>');
$body = substr($body, 0, $pos) . substr($body, $pos2, strlen($body)-$pos2);
$body_lc = strtolower($body);
}
}
// replace event handlers on any object
while ($body != $prev_body)
{
$prev_body = $body;
$body = preg_replace('/(<[^!][^>]*\s)(on[^=>]+)=([^>]+>)/im', '$1__removed=$3', $body);
$body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
}
// resolve <base href>
$base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i';
if (preg_match($base_reg, $body, $regs))
{
$base_url = $regs[2];
$body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
$body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
$body = preg_replace($base_reg, '', $body);
}
// modify HTML links to open a new window if clicked
$body = preg_replace('/<a\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1');", $body);
// add comments arround html and other tags
$out = preg_replace(array('/(<\/?html[^>]*>)/i',
'/(<\/?head[^>]*>)/i',
'/(<title[^>]*>.*<\/title>)/Ui',
'/(<\/?meta[^>]*>)/i'),
'<!--\\1-->',
$body);
$out = preg_replace(array('/(<body[^>]*>)/i',
'/(<\/body>)/i'),
array('<div class="rcmBody">',
'</div>'),
$out);
return $out;
}
// parse link attributes and set correct target
function rcmail_alter_html_link($in)
{
$in = preg_replace('/=([^("|\s)]+)(\s|$)/', '="\1"', $in);
$attrib = parse_attrib_string($in);
if (stristr((string)$attrib['href'], 'mailto:'))
$attrib['onclick'] = sprintf("return %s.command('compose','%s',this)",
$GLOBALS['JS_OBJECT_NAME'],
JQ(substr($attrib['href'], 7)));
else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
$attrib['target'] = '_blank';
return '<a' . create_attrib_string($attrib, array('href', 'name', 'target', 'onclick', 'id', 'class', 'style', 'title')) . '>';
}
// replace all css definitions with #container [def]
function rcmail_mod_css_styles($source, $container_id)
{
$a_css_values = array();
$last_pos = 0;
// cut out all contents between { and }
while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
{
$key = sizeof($a_css_values);
$a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
$source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
$last_pos = $pos+2;
}
// remove html commends and add #container to each tag selector.
// also replace body definition because we also stripped off the <body> tag
$styles = preg_replace(array('/(^\s*<!--)|(-->\s*$)/', '/(^\s*|,\s*|\}\s*)([a-z0-9\._][a-z0-9\.\-_]*)/im', '/<<str_replacement\[([0-9]+)\]>>/e', "/$container_id\s+body/i"),
array('', "\\1#$container_id \\2", "\$a_css_values[\\1]", "$container_id div.rcmBody"),
$source);
return $styles;
}
function rcmail_has_html_part($message_parts)
{
if (!is_array($message_parts))
return FALSE;
// check all message parts
foreach ($message_parts as $pid => $part)
{
$mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
if ($mimetype=='text/html')
{
return TRUE;
}
}
return FALSE;
}
// return first HTML part of a message
function rcmail_first_html_part($message_struct)
{
global $IMAP;
if (!is_array($message_struct['parts']))
return FALSE;
$html_part = NULL;
// check all message parts
foreach ($message_struct['parts'] as $pid => $part)
{
$mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
if ($mimetype=='text/html')
{
$html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
}
}
if ($html_part)
{
// remove special chars encoding
//$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
//$html_part = strtr($html_part, $trans);
return $html_part;
}
return FALSE;
}
// return first text part of a message
function rcmail_first_text_part($message_struct)
{
global $IMAP;
if (empty($message_struct['parts']))
return $message_struct['UID'] ? $IMAP->get_body($message_struct['UID']) : false;
// check all message parts
foreach ($message_struct['parts'] as $pid => $part)
{
$mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
if ($mimetype=='text/plain')
return $IMAP->get_message_part($message_struct['UID'], $pid, $part);
else if ($mimetype=='text/html')
{
$html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
// remove special chars encoding
$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
$html_part = strtr($html_part, $trans);
// create instance of html2text class
$txt = new html2text($html_part);
return $txt->get_text();
}
}
return FALSE;
}
// decode address string and re-format it as HTML links
function rcmail_address_string($input, $max=NULL, $addicon=NULL)
{
global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $EMAIL_ADDRESS_PATTERN;
$a_parts = $IMAP->decode_address_list($input);
if (!sizeof($a_parts))
return $input;
$c = count($a_parts);
$j = 0;
$out = '';
foreach ($a_parts as $part)
{
$j++;
if ($PRINT_MODE)
$out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
{
$out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
$part['mailto'],
$JS_OBJECT_NAME,
$part['mailto'],
$part['mailto'],
Q($part['name']));
if ($addicon)
$out .= sprintf('&nbsp;<a href="#add" onclick="return %s.command(\'add-contact\',\'%s\',this)" title="%s"><img src="%s%s" alt="add" border="0" /></a>',
$JS_OBJECT_NAME,
urlencode($part['string']),
rcube_label('addtoaddressbook'),
$CONFIG['skin_path'],
$addicon);
}
else
{
if ($part['name'])
$out .= Q($part['name']);
if ($part['mailto'])
$out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', $part['mailto']);
}
if ($c>$j)
$out .= ','.($max ? '&nbsp;' : ' ');
if ($max && $j==$max && $c>$j)
{
$out .= '...';
break;
}
}
return $out;
}
function rcmail_message_part_controls()
{
global $CONFIG, $IMAP, $MESSAGE;
if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$_GET['_part']])
return '';
$part = &$MESSAGE['parts'][$_GET['_part']];
$attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
$out = '<table '. $attrib_str . ">\n";
$filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name'];
$filesize = $part->size;
if ($filename)
{
$out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
Q(rcube_label('filename')),
Q(rcube_imap::decode_mime_string($filename)),
str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
Q(rcube_label('download')));
}
if ($filesize)
$out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
Q(rcube_label('filesize')),
show_bytes($filesize));
$out .= "\n</table>";
return $out;
}
function rcmail_message_part_frame($attrib)
{
global $MESSAGE;
$part = $MESSAGE['parts'][$_GET['_part']];
$ctype_primary = strtolower($part->ctype_primary);
$attrib['src'] = './?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
$attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
$out = '<iframe '. $attrib_str . "></iframe>";
return $out;
}
// clear message composing settings
function rcmail_compose_cleanup()
{
if (!isset($_SESSION['compose']))
return;
// remove attachment files from temp dir
if (is_array($_SESSION['compose']['attachments']))
foreach ($_SESSION['compose']['attachments'] as $attachment)
@unlink($attachment['path']);
unset($_SESSION['compose']);
}
?>
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Thu, Mar 19, 9:08 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458585
Default Alt Text
(71 KB)

Event Timeline