Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F9121947
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
100 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CHANGELOG b/CHANGELOG
index f78d3dc9a..54830f1dd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,724 +1,731 @@
CHANGELOG RoundCube Webmail
---------------------------
+2007/05/28 (thomasb)
+---------
+- Fixed buggy imap_root settings (closes #1484379)
+- Prevent default events on subject links (#1484399)
+- Typo in rcube_smtp.inc
+
+
2007/05/23 (estadtherr)
----------
- Upgrade to TinyMCE v2.1.1.1
2007/05/18 (thomasb)
----------
- Use HTTP-POST requests for actions that change state
2007/05/17 (thomasb)
----------
- Updated Catalan, Russian, Portuguese, Slovak and Chinese translations
- Renamed localization folder for Chinese (Big5)
- Chanegd Slovenian language code from 'si' to 'sl'
- Added Sinhala (Sri-Lanka) localization
- Use global filters and bind username/ for Ldap searches (#1484159)
- Hide quota display if imap server does not support it
- Hide address groups if no LDAP servers configured
- Add link to message subjects (closes #1484257)
- Better SQL query for contact listing/search (closes #1484369)
2007/05/13 (thomasb)
----------
- Updated Norwegian (bokmal), Czech, Danish and Portuguese (standard) translation
- Fixed marking as read in preview pane (closes #1484364)
- CSS hack to display attachments correctly in IE6
- Wrap message body text (closes #1484148)
2007/05/03 (yllar)
----------
- Updated French, Lithuanian, Armenian, Spanish and Italian translations
2007/05/01 (thomasb)
----------
- Updated German, Euskara, Hungarian, Romanian and Spanish translation
- Added Hindi and Kurdish localization
2007/04/28 (thomasb)
----------
- LDAP access is back in address book (closes #1484087)
- Added search function for contacts
- New Template parsing and output encoding
- Fixed bugs #1484119 and #1483978
2007/04/08 (thomasb)
----------
- Fixed message moving procedure (closes #1484308)
- Fixed display of multiple attachments (closes #1466563)
- Fixed check for new messages (closes #1484310)
- List attachments without filename
2007/03/27 (thomasb)
----------
- New session authentication: Change sessid cookie when login, authentication with sessauth cookie is now configurable.
Should close bugs #1483951 and #1484299
2007/03/23 (thomasb)
----------
- Correctly translate mailbox names (closes #1484276)
- Quote e-mail address links (closes #1484300)
2007/03/21 (thomasb)
----------
- Updated PEAR::Mail_mime package
- Added Persian localization
- Updated Catalan and Brazilian Portuguese translations
- Updated INSTALL manual with a note about database passwords
- Accept single quotes for HTML attributes when modifying message body (thanks Jason)
- Sanitize input for new users/identities (thanks Colin Alston)
2007/03/19 (thomasb)
----------
- Don't download HTML message parts
- Convert HTML parts to plaintext if 'prefer_html' is off
- Correctly parse message/rfc822 parts (closes #1484045)
- Code cleanup
2007/03/18 (thomasb)
----------
- Also use user_id for unique key in messages table (closes #1484074)
- Hide contacts drop down on blur (closes #1484203)
- Make entries in contacts drop down clickable
- Turn off browser autocompletion on login page
- Quote <? in text/html message parts
- Hide border around radio buttons
- Replaced old JS function calls.
2007/03/13 (thomasb)
----------
- Applied patch for attachment download by crichardson (closes #1484198)
- Fixed bug in Postgres DB handling (closes #1484068)
- Fixed bug of invalid calls to fetchRow() in rcube_db.inc (closes #1484280)
- Fixed array_merge bug (closes #1484281)
- Fixed flag for deletion in list view (closes #1484264)
- Finally support semicolons as recipient separator (closes ##1484251)
- Fixed message headers (subject) encoding
2007/03/04 (tomekp)
----------
- check if safe mode is on or not (closes #1484269)
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/include/rcube_smtp.inc b/program/include/rcube_smtp.inc
index 6953e2666..2ddb5cf17 100644
--- a/program/include/rcube_smtp.inc
+++ b/program/include/rcube_smtp.inc
@@ -1,361 +1,361 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/rcube_smtp.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Provide SMTP functionality using socket connections |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// include required PEAR classes
require_once('Net/SMTP.php');
// define headers delimiter
define('SMTP_MIME_CRLF', "\r\n");
$SMTP_CONN = null;
/**
* Function for sending mail using SMTP.
*
* @param string Sender e-Mail address
*
* @param mixed Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid. This may contain recipients not
* specified in the headers, for Bcc:, resending
* messages, etc.
*
* @param mixed The message headers to send with the mail
* Either as an associative array or a finally
* formatted string
*
* @param string The full text of the message body, including any Mime parts, etc.
*
* @return bool Returns TRUE on success, or FALSE on error
* @access public
*/
function smtp_mail($from, $recipients, &$headers, &$body, &$response)
{
global $SMTP_CONN, $CONFIG;
$smtp_timeout = null;
$smtp_host = $CONFIG['smtp_server'];
$smtp_port = is_numeric($CONFIG['smtp_port']) ? $CONFIG['smtp_port'] : 25;
$smtp_host_url = parse_url($CONFIG['smtp_server']);
// overwrite port
if ($smtp_host_url['host'] && $smtp_host_url['port'])
{
$smtp_host = $smtp_host_url['host'];
$smtp_port = $smtp_host_url['port'];
}
// re-write smtp host
if ($smtp_host_url['host'] && $smtp_host_url['scheme'])
$smtp_host = sprintf('%s://%s', $smtp_host_url['scheme'], $smtp_host_url['host']);
// create Net_SMTP object and connect to server
if (!is_object($smtp_conn))
{
- $helo_host = !empty($_SERVER['server_name']) ? $_SERVER['server_name'] : 'localhost';
+ $helo_host = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost';
$SMTP_CONN = new Net_SMTP($smtp_host, $smtp_port, $helo_host);
// set debugging
if ($CONFIG['debug_level'] & 8)
$SMTP_CONN->setDebug(TRUE);
// try to connect to server and exit on failure
$result = $SMTP_CONN->connect($smtp_timeout);
if (PEAR::isError($result))
{
$SMTP_CONN = null;
$response[] = "Connection failed: ".$result->getMessage();
return FALSE;
}
// attempt to authenticate to the SMTP server
if ($CONFIG['smtp_user'] && $CONFIG['smtp_pass'])
{
if (strstr($CONFIG['smtp_user'], '%u'))
$smtp_user = str_replace('%u', $_SESSION['username'], $CONFIG['smtp_user']);
else
$smtp_user = $CONFIG['smtp_user'];
if (strstr($CONFIG['smtp_pass'], '%p'))
$smtp_pass = str_replace('%p', decrypt_passwd($_SESSION['password']), $CONFIG['smtp_pass']);
else
$smtp_pass = $CONFIG['smtp_pass'];
$smtp_auth_type = empty($CONFIG['smtp_auth_type']) ? NULL : $CONFIG['smtp_auth_type'];
$result = $SMTP_CONN->auth($smtp_user, $smtp_pass, $smtp_auth_type);
if (PEAR::isError($result))
{
smtp_reset();
$response[] .= "Authentication failure: ".$result->getMessage();
return FALSE;
}
}
}
// prepare message headers as string
if (is_array($headers))
{
$headerElements = smtp_prepare_headers($headers);
if (!$headerElements)
{
smtp_reset();
return FALSE;
}
list($from, $text_headers) = $headerElements;
}
else if (is_string($headers))
$text_headers = $headers;
else
{
smtp_reset();
$response[] .= "Invalid message headers";
return FALSE;
}
// exit if no from address is given
if (!isset($from))
{
smtp_reset();
$response[] .= "No From address has been provided";
return FALSE;
}
// set From: address
if (PEAR::isError($SMTP_CONN->mailFrom($from)))
{
smtp_reset();
$response[] .= "Failed to set sender '$from'";
return FALSE;
}
// prepare list of recipients
$recipients = smtp_parse_rfc822($recipients);
if (PEAR::isError($recipients))
{
smtp_reset();
return FALSE;
}
// set mail recipients
foreach ($recipients as $recipient)
{
if (PEAR::isError($SMTP_CONN->rcptTo($recipient)))
{
smtp_reset();
$response[] .= "Failed to add recipient '$recipient'";
return FALSE;
}
}
// Concatenate headers and body so it can be passed by reference to SMTP_CONN->data
// so preg_replace in SMTP_CONN->quotedata will store a reference instead of a copy.
// We are still forced to make another copy here for a couple ticks so we don't really
// get to save a copy in the method call.
$data = $text_headers . "\r\n" . $body;
// unset old vars to save data and so we can pass into SMTP_CONN->data by reference.
unset($text_headers, $body);
// Send the message's headers and the body as SMTP data.
if (PEAR::isError($SMTP_CONN->data($data)))
{
smtp_reset();
$response[] .= "Failed to send data";
return FALSE;
}
$response[] = join(': ', $SMTP_CONN->getResponse());
return TRUE;
}
/**
* Reset the global SMTP connection
* @access public
*/
function smtp_reset()
{
global $SMTP_CONN;
if (is_object($SMTP_CONN))
{
$SMTP_CONN->rset();
smtp_disconnect();
}
}
/**
* Disconnect the global SMTP connection and destroy object
* @access public
*/
function smtp_disconnect()
{
global $SMTP_CONN;
if (is_object($SMTP_CONN))
{
$SMTP_CONN->disconnect();
$SMTP_CONN = null;
}
}
/**
* Take an array of mail headers and return a string containing
* text usable in sending a message.
*
* @param array $headers The array of headers to prepare, in an associative
* array, where the array key is the header name (ie,
* 'Subject'), and the array value is the header
* value (ie, 'test'). The header produced from those
* values would be 'Subject: test'.
*
* @return mixed Returns false if it encounters a bad address,
* otherwise returns an array containing two
* elements: Any From: address found in the headers,
* and the plain text version of the headers.
* @access private
*/
function smtp_prepare_headers($headers)
{
$lines = array();
$from = null;
foreach ($headers as $key => $value)
{
if (strcasecmp($key, 'From') === 0)
{
$addresses = smtp_parse_rfc822($value);
if (is_array($addresses))
$from = $addresses[0];
// Reject envelope From: addresses with spaces.
if (strstr($from, ' '))
return FALSE;
$lines[] = $key . ': ' . $value;
}
else if (strcasecmp($key, 'Received') === 0)
{
$received = array();
if (is_array($value))
{
foreach ($value as $line)
$received[] = $key . ': ' . $line;
}
else
{
$received[] = $key . ': ' . $value;
}
// Put Received: headers at the top. Spam detectors often
// flag messages with Received: headers after the Subject:
// as spam.
$lines = array_merge($received, $lines);
}
else
{
// If $value is an array (i.e., a list of addresses), convert
// it to a comma-delimited string of its elements (addresses).
if (is_array($value))
$value = implode(', ', $value);
$lines[] = $key . ': ' . $value;
}
}
return array($from, join(SMTP_MIME_CRLF, $lines) . SMTP_MIME_CRLF);
}
/**
* Take a set of recipients and parse them, returning an array of
* bare addresses (forward paths) that can be passed to sendmail
* or an smtp server with the rcpt to: command.
*
* @param mixed Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid.
*
* @return array An array of forward paths (bare addresses).
* @access private
*/
function smtp_parse_rfc822($recipients)
{
// if we're passed an array, assume addresses are valid and implode them before parsing.
if (is_array($recipients))
$recipients = implode(', ', $recipients);
$addresses = array();
$recipients = smtp_explode_quoted_str(",", $recipients);
reset($recipients);
while (list($k, $recipient) = each($recipients))
{
$a = explode(" ", $recipient);
while (list($k2, $word) = each($a))
{
if ((strpos($word, "@") > 0) && (strpos($word, "\"")===false))
{
$word = ereg_replace('^<|>$', '', trim($word));
if (in_array($word, $addresses)===false)
array_push($addresses, $word);
}
}
}
return $addresses;
}
function smtp_explode_quoted_str($delimiter, $string)
{
$quotes=explode("\"", $string);
while ( list($key, $val) = each($quotes))
if (($key % 2) == 1)
$quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
$string=implode("\"", $quotes);
$result=explode($delimiter, $string);
while (list($key, $val) = each($result))
$result[$key] = str_replace("_!@!_", $delimiter, $result[$key]);
return $result;
}
?>
diff --git a/program/js/list.js b/program/js/list.js
index 23a9c349c..c4f02402b 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -1,711 +1,717 @@
/*
+-----------------------------------------------------------------------+
| RoundCube List Widget |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2006, RoundCube Dev, - Switzerland |
| Licensed under the GNU GPL |
| |
+-----------------------------------------------------------------------+
| Authors: Thomas Bruederli <roundcube@gmail.com> |
| Charles McNulty <charles@charlesmcnulty.com> |
+-----------------------------------------------------------------------+
| Requires: common.js |
+-----------------------------------------------------------------------+
$Id: list.js 344 2006-09-18 03:49:28Z thomasb $
*/
/**
* RoundCube List Widget class
* @contructor
*/
function rcube_list_widget(list, p)
{
// static contants
this.ENTER_KEY = 13;
this.DELETE_KEY = 46;
this.list = list ? list : null;
this.frame = null;
this.rows = [];
this.selection = [];
this.shiftkey = false;
this.multiselect = false;
this.draggable = false;
this.keyboard = false;
this.dont_select = false;
this.drag_active = false;
this.last_selected = 0;
+ this.shift_start = 0;
this.in_selection_before = false;
this.focused = false;
this.drag_mouse_start = null;
this.dblclick_time = 600;
this.row_init = function(){};
this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragend:[] };
// overwrite default paramaters
if (p && typeof(p)=='object')
for (var n in p)
this[n] = p[n];
}
rcube_list_widget.prototype = {
/**
* get all message rows from HTML table and init each row
*/
init: function()
{
if (this.list && this.list.tBodies[0])
{
this.rows = new Array();
var row;
for(var r=0; r<this.list.tBodies[0].childNodes.length; r++)
{
row = this.list.tBodies[0].childNodes[r];
while (row && (row.nodeType != 1 || row.style.display == 'none'))
{
row = row.nextSibling;
r++;
}
this.init_row(row);
}
this.frame = this.list.parentNode;
// set body events
if (this.keyboard)
rcube_event.add_listener({element:document, event:'keydown', object:this, method:'key_press'});
}
},
/**
*
*/
init_row: function(row)
{
// make references in internal array and set event handlers
if (row && String(row.id).match(/rcmrow([a-z0-9\-_=]+)/i))
{
var p = this;
var uid = RegExp.$1;
row.uid = uid;
this.rows[uid] = {uid:uid, id:row.id, obj:row, classname:row.className};
// set eventhandlers to table row
row.onmousedown = function(e){ return p.drag_row(e, this.uid); };
row.onmouseup = function(e){ return p.click_row(e, this.uid); };
if (document.all)
row.onselectstart = function() { return false; };
this.row_init(this.rows[uid]);
}
},
/**
*
*/
clear: function(sel)
{
var tbody = document.createElement('TBODY');
this.list.insertBefore(tbody, this.list.tBodies[0]);
this.list.removeChild(this.list.tBodies[1]);
this.rows = new Array();
if (sel) this.clear_selection();
},
/**
* 'remove' message row from list (just hide it)
*/
remove_row: function(uid)
{
if (this.rows[uid].obj)
this.rows[uid].obj.style.display = 'none';
this.rows[uid] = null;
},
/**
*
*/
insert_row: function(row, attop)
{
var tbody = this.list.tBodies[0];
if (attop && tbody.rows.length)
tbody.insertBefore(row, tbody.firstChild);
else
tbody.appendChild(row);
this.init_row(row);
},
/**
* Set focur to the list
*/
focus: function(e)
{
this.focused = true;
for (var n=0; n<this.selection.length; n++)
{
id = this.selection[n];
if (this.rows[id].obj)
{
this.set_classname(this.rows[id].obj, 'selected', true);
this.set_classname(this.rows[id].obj, 'unfocused', false);
}
}
if (e || (e = window.event))
rcube_event.cancel(e);
},
/**
* remove focus from the list
*/
blur: function()
{
var id;
this.focused = false;
for (var n=0; n<this.selection.length; n++)
{
id = this.selection[n];
if (this.rows[id] && this.rows[id].obj)
{
this.set_classname(this.rows[id].obj, 'selected', false);
this.set_classname(this.rows[id].obj, 'unfocused', true);
}
}
},
/**
* onmousedown-handler of message list row
*/
drag_row: function(e, id)
{
this.in_selection_before = this.in_selection(id) ? id : false;
// don't do anything (another action processed before)
if (this.dont_select)
return false;
// selects currently unselected row
if (!this.in_selection_before)
{
var mod_key = rcube_event.get_modifier(e);
this.select_row(id, mod_key, false);
}
if (this.draggable && this.selection.length)
{
this.drag_start = true;
- this.drag_mouse_start = rcube_event.get_mouse_pos(e);
+ this.drag_mouse_start = rcube_event.get_mouse_pos(e);
rcube_event.add_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'});
rcube_event.add_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'});
}
return false;
},
/**
* onmouseup-handler of message list row
*/
click_row: function(e, id)
{
var now = new Date().getTime();
var mod_key = rcube_event.get_modifier(e);
// don't do anything (another action processed before)
if (this.dont_select)
{
this.dont_select = false;
return false;
}
var dblclicked = now - this.rows[id].clicked < this.dblclick_time;
// unselects currently selected row
if (!this.drag_active && this.in_selection_before == id && !dblclicked)
this.select_row(id, mod_key, false);
this.drag_start = false;
this.in_selection_before = false;
// row was double clicked
if (this.rows && dblclicked && this.in_selection(id))
this.trigger_event('dblclick');
else
this.trigger_event('click');
if (!this.drag_active)
rcube_event.cancel(e);
this.rows[id].clicked = now;
return false;
},
/**
* get next and previous rows that are not hidden
*/
get_next_row: function()
{
if (!this.rows)
return false;
var last_selected_row = this.rows[this.last_selected];
var new_row = last_selected_row ? last_selected_row.obj.nextSibling : null;
while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none'))
new_row = new_row.nextSibling;
return new_row;
},
get_prev_row: function()
{
if (!this.rows)
return false;
var last_selected_row = this.rows[this.last_selected];
var new_row = last_selected_row ? last_selected_row.obj.previousSibling : null;
while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none'))
new_row = new_row.previousSibling;
return new_row;
},
// selects or unselects the proper row depending on the modifier key pressed
select_row: function(id, mod_key, with_mouse)
{
var select_before = this.selection.join(',');
if (!this.multiselect)
mod_key = 0;
+
+ if (!this.shift_start)
+ this.shift_start = id
if (!mod_key)
{
this.shift_start = id;
this.highlight_row(id, false);
}
else
{
switch (mod_key)
{
case SHIFT_KEY:
- this.shift_select(id, false);
+ this.shift_select(id, false);
break;
case CONTROL_KEY:
- this.shift_start = id;
if (!with_mouse)
- this.highlight_row(id, true);
+ this.highlight_row(id, true);
break;
case CONTROL_SHIFT_KEY:
this.shift_select(id, true);
break;
default:
- this.highlight_row(id, false);
+ this.highlight_row(id, false);
break;
}
}
// trigger event if selection changed
if (this.selection.join(',') != select_before)
this.trigger_event('select');
if (this.last_selected != 0 && this.rows[this.last_selected])
this.set_classname(this.rows[this.last_selected].obj, 'focused', false);
+
+ if (!this.selection.length)
+ this.shift_start = null;
this.last_selected = id;
this.set_classname(this.rows[id].obj, 'focused', true);
},
/**
* Alias method for select_row
*/
select: function(id)
{
this.select_row(id, false);
this.scrollto(id);
},
/**
* Select row next to the last selected one.
* Either below or above.
*/
select_next: function()
{
var next_row = this.get_next_row();
var prev_row = this.get_prev_row();
var new_row = (next_row) ? next_row : prev_row;
if (new_row)
this.select_row(new_row.uid, false, false);
},
/**
* Perform selection when shift key is pressed
*/
shift_select: function(id, control)
{
var from_rowIndex = this.rows[this.shift_start].obj.rowIndex;
var to_rowIndex = this.rows[id].obj.rowIndex;
var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex);
var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex);
// iterate through the entire message list
for (var n in this.rows)
{
if ((this.rows[n].obj.rowIndex >= i) && (this.rows[n].obj.rowIndex <= j))
{
if (!this.in_selection(n))
this.highlight_row(n, true);
}
else
{
if (this.in_selection(n) && !control)
this.highlight_row(n, true);
}
}
},
/**
* Check if given id is part of the current selection
*/
in_selection: function(id)
{
for(var n in this.selection)
if (this.selection[n]==id)
return true;
return false;
},
/**
* Select each row in list
*/
select_all: function(filter)
{
if (!this.rows || !this.rows.length)
return false;
// reset selection first
this.clear_selection();
for (var n in this.rows)
{
if (!filter || this.rows[n][filter]==true)
{
this.last_selected = n;
this.highlight_row(n, true);
}
}
return true;
},
/**
* Unselect all selected rows
*/
clear_selection: function()
{
for(var n=0; n<this.selection.length; n++)
if (this.rows[this.selection[n]])
{
this.set_classname(this.rows[this.selection[n]].obj, 'selected', false);
this.set_classname(this.rows[this.selection[n]].obj, 'unfocused', false);
}
this.selection = new Array();
},
/**
* Getter for the selection array
*/
get_selection: function()
{
return this.selection;
},
/**
* Return the ID if only one row is selected
*/
get_single_selection: function()
{
if (this.selection.length == 1)
return this.selection[0];
else
return null;
},
/**
* Highlight/unhighlight a row
*/
highlight_row: function(id, multiple)
{
if (this.rows[id] && !multiple)
{
this.clear_selection();
this.selection[0] = id;
this.set_classname(this.rows[id].obj, 'selected', true)
}
else if (this.rows[id])
{
if (!this.in_selection(id)) // select row
{
this.selection[this.selection.length] = id;
this.set_classname(this.rows[id].obj, 'selected', true);
}
else // unselect row
{
var p = find_in_array(id, this.selection);
var a_pre = this.selection.slice(0, p);
var a_post = this.selection.slice(p+1, this.selection.length);
this.selection = a_pre.concat(a_post);
this.set_classname(this.rows[id].obj, 'selected', false);
this.set_classname(this.rows[id].obj, 'unfocused', false);
}
}
},
/**
* Handler for keyboard events
*/
key_press: function(e)
{
if (this.focused != true)
return true;
this.shiftkey = e.shiftKey;
var keyCode = document.layers ? e.which : document.all ? event.keyCode : document.getElementById ? e.keyCode : 0;
var mod_key = rcube_event.get_modifier(e);
switch (keyCode)
{
case 40:
case 38:
return this.use_arrow_key(keyCode, mod_key);
break;
default:
this.key_pressed = keyCode;
this.trigger_event('keypress');
}
return true;
},
/**
* Special handling method for arrow keys
*/
use_arrow_key: function(keyCode, mod_key)
{
var new_row;
if (keyCode == 40) // down arrow key pressed
new_row = this.get_next_row();
else if (keyCode == 38) // up arrow key pressed
new_row = this.get_prev_row();
if (new_row)
{
this.select_row(new_row.uid, mod_key, true);
this.scrollto(new_row.uid);
}
return false;
},
/**
* Try to scroll the list to make the specified row visible
*/
scrollto: function(id)
{
var row = this.rows[id].obj;
if (row && this.frame)
{
var scroll_to = Number(row.offsetTop);
if (scroll_to < Number(this.frame.scrollTop))
this.frame.scrollTop = scroll_to;
else if (scroll_to + Number(row.offsetHeight) > Number(this.frame.scrollTop) + Number(this.frame.offsetHeight))
this.frame.scrollTop = (scroll_to + Number(row.offsetHeight)) - Number(this.frame.offsetHeight);
}
},
/**
* Handler for mouse move events
*/
drag_mouse_move: function(e)
{
if (this.drag_start)
{
// check mouse movement, of less than 3 pixels, don't start dragging
var m = rcube_event.get_mouse_pos(e);
if (!this.drag_mouse_start || (Math.abs(m.x - this.drag_mouse_start.x) < 3 && Math.abs(m.y - this.drag_mouse_start.y) < 3))
return false;
if (!this.draglayer)
this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000});
// get subjects of selectedd messages
var names = '';
var c, node, subject, obj;
for(var n=0; n<this.selection.length; n++)
{
if (n>12) // only show 12 lines
{
names += '...';
break;
}
if (this.rows[this.selection[n]].obj)
{
obj = this.rows[this.selection[n]].obj;
subject = '';
for(c=0; c<obj.childNodes.length; c++)
if (obj.childNodes[c].nodeName=='TD' && (node = obj.childNodes[c].firstChild) && (node.nodeType==3 || node.nodeName=='A'))
{
subject = node.nodeType==3 ? node.data : node.innerHTML;
names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />';
break;
}
}
}
this.draglayer.write(names);
this.draglayer.show(1);
this.drag_active = true;
this.trigger_event('dragstart');
}
if (this.drag_active && this.draglayer)
{
var pos = rcube_event.get_mouse_pos(e);
this.draglayer.move(pos.x+20, pos.y-5);
}
this.drag_start = false;
return false;
},
/**
* Handler for mouse up events
*/
drag_mouse_up: function(e)
{
document.onmousemove = null;
if (this.draglayer && this.draglayer.visible)
this.draglayer.show(0);
this.drag_active = false;
this.trigger_event('dragend');
rcube_event.remove_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'});
rcube_event.remove_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'});
return rcube_event.cancel(e);
},
/**
* set/unset a specific class name
*/
set_classname: function(obj, classname, set)
{
var reg = new RegExp('\s*'+classname, 'i');
if (!set && obj.className.match(reg))
obj.className = obj.className.replace(reg, '');
else if (set && !obj.className.match(reg))
obj.className += ' '+classname;
},
/**
* Setter for object event handlers
*
* @param {String} Event name
* @param {Function} Handler function
* @return Listener ID (used to remove this handler later on)
*/
addEventListener: function(evt, handler)
{
if (this.events[evt]) {
var handle = this.events[evt].length;
this.events[evt][handle] = handler;
return handle;
}
else
return false;
},
/**
* Removes a specific event listener
*
* @param {String} Event name
* @param {Int} Listener ID to remove
*/
removeEventListener: function(evt, handle)
{
if (this.events[evt] && this.events[evt][handle])
this.events[evt][handle] = null;
},
/**
* This will execute all registered event handlers
* @private
*/
trigger_event: function(evt)
{
if (this.events[evt] && this.events[evt].length) {
for (var i=0; i<this.events[evt].length; i++)
if (typeof(this.events[evt][i]) == 'function')
this.events[evt][i](this);
}
}
};
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index cc1187bd9..4267b48fe 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1,1495 +1,1495 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/mail/func.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005-2007, 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($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
$IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
// define url for getting message parts
if (strlen($_GET['_uid']))
$GET_URL = rcmail_url('get', array('_mbox'=>$IMAP->get_mailbox_name(), '_uid'=>get_input_value('_uid', RCUBE_INPUT_GET)));
// set current mailbox in client environment
$OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
$OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
if ($CONFIG['trash_mbox'])
$OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
if ($CONFIG['drafts_mbox'])
$OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
if ($CONFIG['junk_mbox'])
$OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
if (!$OUTPUT->ajax_call)
rcube_add_label('checkingmail');
// return the mailboxlist in HTML
function rcmail_mailbox_list($attrib)
{
global $IMAP, $CONFIG, $OUTPUT, $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_gui_object('mailboxlist', $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 $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_id = preg_replace('/[^A-Za-z0-9\-_]/', '', $folder['id']);
$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="rcmli%s" class="mailbox %s %s%s%s"><a href="%s"'.
' onclick="return %s.command(\'list\',\'%s\',this)"'.
' onmouseover="return %s.focus_folder(\'%s\')"' .
' onmouseout="return %s.unfocus_folder(\'%s\')"' .
' onmouseup="return %s.folder_mouse_up(\'%s\')"%s>%s</a>',
$folder_id,
$class_name,
$zebra_class,
$unread_count ? ' unread' : '',
$folder['id']==$mbox_name ? ' selected' : '',
Q(rcmail_url('', array('_mbox' => $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(' ', $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;
$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');
$mbox = $IMAP->get_mailbox_name();
// show 'to' instead of from in sent messages
if (($mbox==$CONFIG['sent_mbox'] || $mbox==$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\"> </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 = ' ';
// asc link
if (!empty($attrib['sortascbutton']))
{
$sort .= $OUTPUT->button(array(
'command' => 'sort',
'prop' => $col.'_ASC',
'image' => $attrib['sortascbutton'],
'align' => 'absmiddle',
'title' => 'sortasc'));
}
// desc link
if (!empty($attrib['sortdescbutton']))
{
$sort .= $OUTPUT->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')
{
$action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
$uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
$cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
if (empty($cont)) $cont = Q(rcube_label('nosubject'));
- $cont = sprintf('<a href="%s" onclick="return false">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
+ $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
}
else if ($col=='size')
$cont = show_bytes($header->$col);
else if ($col=='date')
$cont = format_date($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
$OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
$OUTPUT->add_gui_object('messagelist', $attrib['id']);
$OUTPUT->set_env('messagecount', $message_count);
$OUTPUT->set_env('current_page', $IMAP->list_page);
$OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
$OUTPUT->set_env('sort_col', $sort_col);
$OUTPUT->set_env('sort_order', $sort_order);
if ($attrib['messageicon'])
$OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
if ($attrib['deletedicon'])
$OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
if ($attrib['unreadicon'])
$OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
if ($attrib['repliedicon'])
$OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
if ($attrib['attachmenticon'])
$OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
$OUTPUT->set_env('messages', $a_js_message_arr);
$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, $OUTPUT;
$a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
$mbox = $IMAP->get_mailbox_name();
// show 'to' instead of from in sent messages
if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
&& (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
$a_show_cols[$f] = 'to';
$OUTPUT->command('set_message_coltypes', $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')
{
$action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
$uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
$cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
if (!$cont) $cont = Q(rcube_label('nosubject'));
- $cont = sprintf('<a href="%s" onclick="return false">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
+ $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
}
else if ($col=='size')
$cont = show_bytes($header->$col);
else if ($col=='date')
$cont = format_date($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;
$OUTPUT->command('add_message_row',
$header->uid,
$a_msg_cols,
$a_msg_flags,
preg_match("/multipart\/m/i", $header->ctype),
$insert_top);
}
}
// return an HTML iframe for loading mail content
function rcmail_messagecontent_frame($attrib)
{
global $OUTPUT;
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->set_env('contentframe', $framename);
$OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
return $out;
}
function rcmail_messagecount_display($attrib)
{
global $IMAP, $OUTPUT;
if (!$attrib['id'])
$attrib['id'] = 'rcmcountdisplay';
$OUTPUT->add_gui_object('countdisplay', $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, $COMM_PATH;
if (!$attrib['id'])
$attrib['id'] = 'rcmquotadisplay';
$OUTPUT->add_gui_object('quotadisplay', $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="./bin/quotaimg.php?u=%s&q=%d&w=%d&h=%d" width="%d" height="%d" alt="%s" title="%s / %s" />',
$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;
$body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
// convert html to text/plain
if ($part->ctype_secondary=='html' && $plain)
{
$txt = new html2text($body, false, true);
$body = $txt->get_text();
$part->ctype_secondary = 'plain';
}
// 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->filename))
{
// 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->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
$sa_inline_objects[] = $mail_part;
}
// is regular attachment
else
{
if (!$mail_part->filename)
$mail_part->filename = 'file_'.$mail_part->mime_id;
$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 ($structure->filename)
$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(rcube_imap::decode_mime_string($headers[$hkey], $headers['charset']));
$out .= "\n<tr>\n";
$out .= '<td class="header-title">'.Q(rcube_label($hkey)).": </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;
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, !$CONFIG['prefer_html']);
$out .= '<div class="message-part">';
if ($part->ctype_secondary != 'plain')
$out .= rcmail_sanitize_html($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&_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->set_env('blockedobjects', true);
$out .= "\n</div>";
return $out;
}
// modify a HTML message that it can be displayed inside a HTML page
function rcmail_sanitize_html($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(
'/(<!DOCTYPE.+)/i',
'/(<\/?html[^>]*>)/i',
'/(<\/?head[^>]*>)/i',
'/(<title[^>]*>.*<\/title>)/Ui',
'/(<\/?meta[^>]*>)/i'),
'<!--\\1-->',
$body);
$out = preg_replace(array('/<body([^>]*)>/i',
'/<\/body>/i'),
array('<div class="rcmBody"\\1>',
'</div>'),
$out);
// quote <? of php and xml files that are specified as text/html
$out = preg_replace(array('/<\?/', '/\?>/'), array('<?', '?>'), $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)",
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, $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 <%s>', 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>',
Q($part['mailto']),
JS_OBJECT_NAME,
JQ($part['mailto']),
Q($part['mailto']),
Q($part['name']));
if ($addicon)
$out .= sprintf(' <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('<%s>', Q($part['mailto']));
}
if ($c>$j)
$out .= ','.($max ? ' ' : ' ');
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";
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($part->filename),
str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
Q(rcube_label('download')));
}
if ($part->size)
$out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
Q(rcube_label('filesize')),
show_bytes($part->size));
$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']);
}
// register UI objects
$OUTPUT->add_handlers(array(
'mailboxlist' => 'rcmail_mailbox_list',
'messages' => 'rcmail_message_list',
'messagecountdisplay' => 'rcmail_messagecount_display',
'quotadisplay' => 'rcmail_quota_display',
'messageheaders' => 'rcmail_message_headers',
'messagebody' => 'rcmail_message_body',
'messagecontentframe' => 'rcmail_messagecontent_frame',
'messagepartframe' => 'rcmail_message_part_frame',
'messagepartcontrols' => 'rcmail_message_part_controls',
'searchform' => 'rcmail_search_form'
));
?>
\ No newline at end of file
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 30, 3:17 AM (16 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1174737
Default Alt Text
(100 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment