Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2571839
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
90 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CHANGELOG b/CHANGELOG
index 839dc4bec..e40b456c0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,596 +1,601 @@
CHANGELOG RoundCube Webmail
---------------------------
+2007/02/25 (estadtherr)
+----------
+- Fixed priority selector issue (#1484150)
+
+
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_shared.inc b/program/include/rcube_shared.inc
index a26fc108b..ba63c825f 100644
--- a/program/include/rcube_shared.inc
+++ b/program/include/rcube_shared.inc
@@ -1,1572 +1,1574 @@
<?php
/*
+-----------------------------------------------------------------------+
| rcube_shared.inc |
| |
| This file is part of the RoundCube PHP suite |
| Copyright (C) 2005-2006, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| CONTENTS: |
| Shared functions and classes used in PHP projects |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// ********* round cube schared classes *********
class rcube_html_page
{
var $css;
var $scripts_path = '';
var $script_files = array();
var $external_scripts = array();
var $scripts = array();
var $charset = 'ISO-8859-1';
var $script_tag_file = "<script type=\"text/javascript\" src=\"%s%s\"></script>\n";
var $script_tag = "<script type=\"text/javascript\">\n<!--\n%s\n\n//-->\n</script>\n";
var $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>";
var $tag_format_external_script = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
var $title = '';
var $header = '';
var $footer = '';
var $body = '';
var $body_attrib = array();
var $meta_tags = array();
// PHP 5 constructor
function __construct()
{
$this->css = new rcube_css();
}
// PHP 4 compatibility
function rcube_html_page()
{
$this->__construct();
}
function include_script($file, $position='head')
{
static $sa_files = array();
if (in_array($file, $sa_files))
return;
if (!is_array($this->script_files[$position]))
$this->script_files[$position] = array();
$this->script_files[$position][] = $file;
}
function include_external_script($script_location, $position='head')
{
if (!is_array($this->external_scripts[$position]))
{
$this->external_scripts[$position] = array();
}
$this->external_scripts[$position][] = $script_location;
}
function add_script($script, $position='head')
{
if (!isset($this->scripts[$position]))
$this->scripts[$position] = "\n$script";
else
$this->scripts[$position] .= "\n$script";
}
function set_title($t)
{
$this->title = $t;
}
function set_charset($charset)
{
global $MBSTRING;
$this->charset = $charset;
if ($MBSTRING && function_exists("mb_internal_encoding"))
{
if(!@mb_internal_encoding($charset))
$MBSTRING = FALSE;
}
}
function get_charset()
{
return $this->charset;
}
function reset()
{
$this->css = new rcube_css();
$this->script_files = array();
$this->scripts = array();
$this->title = '';
}
function write($templ='', $base_path='')
{
$output = empty($templ) ? $this->default_template : trim($templ);
// set default page title
if (empty($this->title))
$this->title = 'RoundCube Mail';
// replace specialchars in content
$__page_title = Q($this->title, 'show', FALSE);
$__page_header = $__page_body = $__page_footer = '';
// include meta tag with charset
if (!empty($this->charset))
{
header('Content-Type: text/html; charset='.$this->charset);
$__page_header = '<meta http-equiv="content-type" content="text/html; charset='.$this->charset.'" />'."\n";
}
// definition of the code to be placed in the document header and footer
if (is_array($this->script_files['head']))
foreach ($this->script_files['head'] as $file)
$__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file);
if (is_array($this->external_scripts['head']))
{
foreach ($this->external_scripts['head'] as $xscript)
{
$__page_header .= sprintf($this->tag_format_external_script, $xscript);
}
}
if (!empty($this->scripts['head']))
$__page_header .= sprintf($this->script_tag, $this->scripts['head']);
if (is_array($this->script_files['foot']))
{
foreach ($this->script_files['foot'] as $file)
$__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file);
}
if (!empty($this->scripts['foot']))
$__page_footer .= sprintf($this->script_tag, $this->scripts['foot']);
if ($this->footer)
$__page_footer .= "\n" . $this->footer;
$__page_header .= $this->css->show();
// find page header
if($hpos = rc_strpos(rc_strtolower($output), '</head>'))
$__page_header .= "\n";
else
{
if (!is_numeric($hpos))
$hpos = rc_strpos(rc_strtolower($output), '<body');
if (!is_numeric($hpos) && ($hpos = rc_strpos(rc_strtolower($output), '<html')))
{
while($output[$hpos]!='>')
$hpos++;
$hpos++;
}
$__page_header = "<head>\n<title>$__page_title</title>\n$__page_header\n</head>\n";
}
// add page hader
if($hpos)
$output = rc_substr($output,0,$hpos) . $__page_header . rc_substr($output,$hpos,rc_strlen($output));
else
$output = $__page_header . $output;
// find page body
if($bpos = rc_strpos(rc_strtolower($output), '<body'))
{
while($output[$bpos]!='>') $bpos++;
$bpos++;
}
else
$bpos = rc_strpos(rc_strtolower($output), '</head>')+7;
// add page body
if($bpos && $__page_body)
$output = rc_substr($output,0,$bpos) . "\n$__page_body\n" . rc_substr($output,$bpos,rc_strlen($output));
// find and add page footer
$output_lc = rc_strtolower($output);
if(($fpos = strrstr($output_lc, '</body>')) ||
($fpos = strrstr($output_lc, '</html>')))
$output = substr($output, 0, $fpos) . "$__page_footer\n" . substr($output, $fpos);
else
$output .= "\n$__page_footer";
// reset those global vars
$__page_header = $__page_footer = '';
// correct absolute paths in images and other tags
$output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output);
$output = str_replace('$__skin_path', $base_path, $output);
print rcube_charset_convert($output, 'UTF-8', $this->charset);
}
function _parse($templ)
{
}
}
class rcube_css
{
var $css_data = array();
var $css_groups = array();
var $include_files = array();
var $grouped_output = TRUE;
var $content_type = 'text/css';
var $base_path = '';
var $indent_chars = "\t";
// add or overwrite a css definition
// either pass porperty and value as separate arguments
// or provide an associative array as second argument
function set_style($selector, $property, $value='')
{
$a_elements = $this->_parse_selectors($selector);
foreach ($a_elements as $element)
{
if (!is_array($property))
$property = array($property => $value);
foreach ($property as $name => $value)
$this->css_data[$element][strtolower($name)] = $value;
}
// clear goups array
$this->css_groups = array();
}
// unset a style property
function remove_style($selector, $property)
{
if (!is_array($property))
$property = array($property);
foreach ($property as $key)
unset($this->css_data[$selector][strtolower($key)]);
// clear goups array
$this->css_groups = array();
}
// define base path for external css files
function set_basepath($path)
{
$this->base_path = preg_replace('/\/$/', '', $path);
}
// enable/disable grouped output
function set_grouped_output($grouped)
{
$this->grouped_output = $grouped;
}
// add a css file as external source
function include_file($filename, $media='')
{
// include multiple files
if (is_array($filename))
{
foreach ($filename as $file)
$this->include_file($file, $media);
}
// add single file
else if (!in_array($filename, $this->include_files))
$this->include_files[] = array('file' => $filename,
'media' => $media);
}
// parse css code
function import_string($str)
{
$ret = FALSE;
if (strlen($str))
$ret = $this->_parse($str);
return $ret;
}
// open and parse a css file
function import_file($file)
{
$ret = FALSE;
if (!is_file($file))
return $ret;
// for php version >= 4.3.0
if (function_exists('file_get_contents'))
$ret = $this->_parse(file_get_contents($file));
// for order php versions
else if ($fp = fopen($file, 'r'))
{
$ret = $this->_parse(fread($fp, filesize($file)));
fclose($fp);
}
return $ret;
}
// copy all properties inherited from superior styles to a specific selector
function copy_inherited_styles($selector)
{
// get inherited props from body and tag/class selectors
$css_props = $this->_get_inherited_styles($selector);
// write modified props back and clear goups array
if (sizeof($css_props))
{
$this->css_data[$selector] = $css_props;
$this->css_groups = array();
}
}
// return css definition for embedding in HTML
function show()
{
$out = '';
// include external css files
if (sizeof($this->include_files))
foreach ($this->include_files as $file_arr)
$out .= sprintf('<link rel="stylesheet" type="%s" href="%s"%s>'."\n",
$this->content_type,
$this->_get_file_path($file_arr['file']),
$file_arr['media'] ? ' media="'.$file_arr['media'].'"' : '');
// compose css string
if (sizeof($this->css_data))
$out .= sprintf("<style type=\"%s\">\n<!--\n\n%s-->\n</style>",
$this->content_type,
$this->to_string());
return $out;
}
// return valid css code of the current styles grid
function to_string($selector=NULL)
{
// return code for a single selector
if ($selector)
{
$indent_str = $this->indent_chars;
$this->indent_chars = '';
$prop_arr = $this->to_array($selector);
$out = $this->_style2string($prop_arr, TRUE);
$this->indent_chars = $indent_str;
}
// compose css code for complete data grid
else
{
$out = '';
$css_data = $this->to_array();
foreach ($css_data as $key => $prop_arr)
$out .= sprintf("%s {\n%s}\n\n",
$key,
$this->_style2string($prop_arr, TRUE));
}
return $out;
}
// return a single-line string of a css definition
function to_inline($selector)
{
if ($this->css_data[$selector])
return str_replace('"', '\\"', $this->_style2string($this->css_data[$selector], FALSE));
}
// return an associative array with selector(s) as key and styles array as value
function to_array($selector=NULL)
{
if (!$selector && $this->grouped_output)
{
// build groups if desired
if (!sizeof($this->css_groups))
$this->_build_groups();
// modify group array to get an array(selector => properties)
$out_arr = array();
foreach ($this->css_groups as $group_arr)
{
$key = join(', ', $group_arr['selectors']);
$out_arr[$key] = $group_arr['properties'];
}
}
else
$out_arr = $this->css_data;
return $selector ? $out_arr[$selector] : $out_arr;
}
// create a css file
function to_file($filepath)
{
if ($fp = fopen($filepath, 'w'))
{
fwrite($fp, $this->to_string());
fclose($fp);
return TRUE;
}
return FALSE;
}
// alias method for import_string() [DEPRECATED]
function add($str)
{
$this->import_string($str);
}
// alias method for to_string() [DEPRECATED]
function get()
{
return $this->to_string();
}
// ******** private methods ********
// parse a string and add styles to internal data grid
function _parse($str)
{
// remove comments
$str = preg_replace("/\/\*(.*)?\*\//Usi", '', $str);
// parse style definitions
if (!preg_match_all ('/([a-z0-9\.#*:_][a-z0-9\.\-_#:*,\[\]\(\)\s\"\'\+\|>~=]+)\s*\{([^\}]*)\}/ims', $str, $matches, PREG_SET_ORDER))
return FALSE;
foreach ($matches as $match_arr)
{
// split selectors into array
$a_keys = $this->_parse_selectors(trim($match_arr[1]));
// parse each property of an element
$codes = explode(";", trim($match_arr[2]));
foreach ($codes as $code)
{
if (strlen(trim($code))>0)
{
// find the property and the value
if (!($sep = strpos($code, ':')))
continue;
$property = strtolower(trim(substr($code, 0, $sep)));
$value = trim(substr($code, $sep+1));
// add the property to the object array
foreach ($a_keys as $key)
$this->css_data[$key][$property] = $value;
}
}
}
// clear goups array
if (sizeof($matches))
{
$this->css_groups = array();
return TRUE;
}
return FALSE;
}
// split selector group
function _parse_selectors($selector)
{
// trim selector and remove multiple spaces
$selector = preg_replace('/\s+/', ' ', trim($selector));
if (strpos($selector, ','))
return preg_split('/[\t\s\n\r]*,[\t\s\n\r]*/mi', $selector);
else
return array($selector);
}
// compare identical styles and make groups
function _build_groups()
{
// clear group array
$this->css_groups = array();
$string_group_map = array();
// bulild css string for each selector and check if the same is already defines
foreach ($this->css_data as $selector => $prop_arr)
{
// make shure to compare props in the same order
ksort($prop_arr);
$compare_str = preg_replace('/[\s\t]+/', '', $this->_style2string($prop_arr, FALSE));
// add selector to extisting group
if (isset($string_group_map[$compare_str]))
{
$group_index = $string_group_map[$compare_str];
$this->css_groups[$group_index]['selectors'][] = $selector;
}
// create new group
else
{
$i = sizeof($this->css_groups);
$string_group_map[$compare_str] = $i;
$this->css_groups[$i] = array('selectors' => array($selector),
'properties' => $this->css_data[$selector]);
}
}
}
// convert the prop array into a valid css definition
function _style2string($prop_arr, $multiline=TRUE)
{
$out = '';
$delm = $multiline ? "\n" : '';
$spacer = $multiline ? ' ' : '';
$indent = $multiline ? $this->indent_chars : '';
if (is_array($prop_arr))
foreach ($prop_arr as $prop => $value)
if (strlen($value))
$out .= sprintf('%s%s:%s%s;%s',
$indent,
$prop,
$spacer,
$value,
$delm);
return $out;
}
// copy all properties inherited from superior styles to a specific selector
function _get_inherited_styles($selector, $loop=FALSE)
{
$css_props = $this->css_data[$selector] ? $this->css_data[$selector] : array();
// get styles from tag selector
if (preg_match('/(([a-z0-9]*)(\.[^\s]+)?)$/i', $selector, $regs))
{
$sel = $regs[1];
$tagname = $regs[2];
$class = $regs[3];
if ($sel && is_array($this->css_data[$sel]))
$css_props = $this->_merge_styles($this->css_data[$sel], $css_props);
if ($class && is_array($this->css_data[$class]))
$css_props = $this->_merge_styles($this->css_data[$class], $css_props);
if ($tagname && is_array($this->css_data[$tagname]))
$css_props = $this->_merge_styles($this->css_data[$tagname], $css_props);
}
// analyse inheritance
if (strpos($selector, ' '))
{
$a_hier = split(' ', $selector);
if (sizeof($a_hier)>1)
{
array_pop($a_hier);
$base_selector = join(' ', $a_hier);
// call this method recursively
$new_props = $this->_get_inherited_styles($base_selector, TRUE);
$css_props = $this->_merge_styles($new_props, $css_props);
}
}
// get body style
if (!$loop && is_array($this->css_data['body']))
$css_props = $this->_merge_styles($this->css_data['body'], $css_props);
return $css_props;
}
// merge two arrays with style properties together like a browser would do
function _merge_styles($one, $two)
{
// these properties are additive
foreach (array('text-decoration') as $prop)
if ($one[$prop] && $two[$prop])
{
// if value contains 'none' it's ignored
if (strstr($one[$prop], 'none'))
continue;
else if (strstr($two[$prop], 'none'))
unset($two[$prop]);
$a_values_one = split(' ', $one[$prop]);
$a_values_two = split(' ', $two[$prop]);
$two[$prop] = join(' ', array_unique(array_merge($a_values_one, $a_values_two)));
}
return array_merge($one, $two);
}
// resolve file path
function _get_file_path($file)
{
if (!$this->base_path && $GLOBALS['CSS_PATH'])
$this->set_basepath($GLOBALS['CSS_PATH']);
$base = ($file{0}=='/' || $file{0}=='.' || substr($file, 0, 7)=='http://') ? '' :
($this->base_path ? $this->base_path.'/' : '');
return $base.$file;
}
}
class base_form_element
{
var $uppertags = FALSE;
var $upperattribs = FALSE;
var $upperprops = FALSE;
var $newline = FALSE;
var $attrib = array();
// create string with attributes
function create_attrib_string($tagname='')
{
if (!sizeof($this->attrib))
return '';
if ($this->name!='')
$this->attrib['name'] = $this->name;
$attrib_arr = array();
foreach ($this->attrib as $key => $value)
{
// don't output some internally used attributes
if (in_array($key, array('form', 'quicksearch')))
continue;
// skip if size if not numeric
if (($key=='size' && !is_numeric($value)))
continue;
// skip empty eventhandlers
if ((strpos($key,'on')===0 && $value==''))
continue;
// encode textarea content
if ($key=='value')
$value = Q($value, 'strict', FALSE);
// attributes with no value
if (in_array($key, array('checked', 'multiple', 'disabled', 'selected')))
{
if ($value)
$attrib_arr[] = $key;
}
// don't convert size of value attribute
else if ($key=='value')
$attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $value, 'value');
// regular tag attributes
else
$attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $this->_conv_case($value, 'value'));
}
return sizeof($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
}
// convert tags and attributes to upper-/lowercase
// $type can either be "tag" or "attrib"
function _conv_case($str, $type='attrib')
{
if ($type == 'tag')
return $this->uppertags ? strtoupper($str) : strtolower($str);
else if ($type == 'attrib')
return $this->upperattribs ? strtoupper($str) : strtolower($str);
else if ($type == 'value')
return $this->upperprops ? strtoupper($str) : strtolower($str);
}
}
class input_field extends base_form_element
{
var $type = 'text';
// PHP 5 constructor
function __construct($attrib=NULL)
{
if (is_array($attrib))
$this->attrib = $attrib;
if ($attrib['type'])
$this->type = $attrib['type'];
if ($attrib['newline'])
$this->newline = TRUE;
}
// PHP 4 compatibility
function input_field($attrib=array())
{
$this->__construct($attrib);
}
// compose input tag
function show($value=NULL, $attrib=NULL)
{
// overwrite object attributes
if (is_array($attrib))
$this->attrib = array_merge($this->attrib, $attrib);
// set value attribute
if ($value!==NULL)
$this->attrib['value'] = $value;
$this->attrib['type'] = $this->type;
// return final tag
return sprintf('<%s%s />%s',
$this->_conv_case('input', 'tag'),
$this->create_attrib_string(),
($this->newline ? "\n" : ""));
}
}
class textfield extends input_field
{
var $type = 'text';
}
class passwordfield extends input_field
{
var $type = 'password';
}
class radiobutton extends input_field
{
var $type = 'radio';
}
class checkbox extends input_field
{
var $type = 'checkbox';
function show($value='', $attrib=NULL)
{
// overwrite object attributes
if (is_array($attrib))
$this->attrib = array_merge($this->attrib, $attrib);
$this->attrib['type'] = $this->type;
if ($value && (string)$value==(string)$this->attrib['value'])
$this->attrib['checked'] = TRUE;
else
$this->attrib['checked'] = FALSE;
// return final tag
return sprintf('<%s%s />%s',
$this->_conv_case('input', 'tag'),
$this->create_attrib_string(),
($this->newline ? "\n" : ""));
}
}
class textarea extends base_form_element
{
// PHP 5 constructor
function __construct($attrib=array())
{
$this->attrib = $attrib;
if ($attrib['newline'])
$this->newline = TRUE;
}
// PHP 4 compatibility
function textarea($attrib=array())
{
$this->__construct($attrib);
}
function show($value='', $attrib=NULL)
{
// overwrite object attributes
if (is_array($attrib))
$this->attrib = array_merge($this->attrib, $attrib);
// take value attribute as content
if ($value=='')
$value = $this->attrib['value'];
// make shure we don't print the value attribute
if (isset($this->attrib['value']))
unset($this->attrib['value']);
if (!empty($value) && !isset($this->attrib['mce_editable']))
$value = Q($value, 'strict', FALSE);
// return final tag
return sprintf('<%s%s>%s</%s>%s',
$this->_conv_case('textarea', 'tag'),
$this->create_attrib_string(),
$value,
$this->_conv_case('textarea', 'tag'),
($this->newline ? "\n" : ""));
}
}
class hiddenfield extends base_form_element
{
var $fields_arr = array();
var $newline = TRUE;
// PHP 5 constructor
function __construct($attrib=NULL)
{
if (is_array($attrib))
$this->add($attrib);
}
// PHP 4 compatibility
function hiddenfield($attrib=NULL)
{
$this->__construct($attrib);
}
// add a hidden field to this instance
function add($attrib)
{
$this->fields_arr[] = $attrib;
}
function show()
{
$out = '';
foreach ($this->fields_arr as $attrib)
{
$this->attrib = $attrib;
$this->attrib['type'] = 'hidden';
$out .= sprintf('<%s%s />%s',
$this->_conv_case('input', 'tag'),
$this->create_attrib_string(),
($this->newline ? "\n" : ""));
}
return $out;
}
}
class select extends base_form_element
{
var $options = array();
/*
syntax:
-------
// create instance. arguments are used to set attributes of select-tag
$select = new select(array('name' => 'fieldname'));
// add one option
$select->add('Switzerland', 'CH');
// add multiple options
$select->add(array('Switzerland', 'Germany'),
array('CH', 'DE'));
// add 10 blank options with 50 chars
// used to fill with javascript (necessary for 4.x browsers)
$select->add_blank(10, 50);
// generate pulldown with selection 'Switzerland' and return html-code
// as second argument the same attributes available to instanciate can be used
print $select->show('CH');
*/
// PHP 5 constructor
function __construct($attrib=NULL)
{
if (is_array($attrib))
$this->attrib = $attrib;
if ($attrib['newline'])
$this->newline = TRUE;
}
// PHP 4 compatibility
function select($attrib=NULL)
{
$this->__construct($attrib);
}
function add($names, $values=NULL)
{
if (is_array($names))
{
foreach ($names as $i => $text)
$this->options[] = array('text' => $text, 'value' => (string)$values[$i]);
}
else
{
$this->options[] = array('text' => $names, 'value' => (string)$values);
}
}
function add_blank($nr, $width=0)
{
$text = $width ? str_repeat(' ', $width) : '';
for ($i=0; $i < $nr; $i++)
$this->options[] = array('text' => $text);
}
function show($select=array(), $attrib=NULL)
{
$options_str = "\n";
$value_str = $this->_conv_case(' value="%s"', 'attrib');
if (!is_array($select))
$select = array((string)$select);
-
+
foreach ($this->options as $option)
{
- $selected = ((!empty($option['value']) && in_array($option['value'], $select, TRUE)) ||
- (in_array($option['text'], $select, TRUE))) ? $this->_conv_case(' selected', 'attrib') : '';
+ $selected = ((isset($option['value']) &&
+ in_array($option['value'], $select, TRUE)) ||
+ (in_array($option['text'], $select, TRUE))) ?
+ $this->_conv_case(' selected', 'attrib') : '';
$options_str .= sprintf("<%s%s%s>%s</%s>\n",
$this->_conv_case('option', 'tag'),
- !empty($option['value']) ? sprintf($value_str, $option['value']) : '',
+ isset($option['value']) ? sprintf($value_str, $option['value']) : '',
$selected,
Q($option['text'], 'strict', FALSE),
$this->_conv_case('option', 'tag'));
}
-
+
// return final tag
return sprintf('<%s%s>%s</%s>%s',
$this->_conv_case('select', 'tag'),
$this->create_attrib_string(),
$options_str,
$this->_conv_case('select', 'tag'),
($this->newline ? "\n" : ""));
}
}
// ********* rcube schared functions *********
// provide details about the client's browser
function rcube_browser()
{
$HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
$bw['ver'] = 0;
$bw['win'] = stristr($HTTP_USER_AGENT, 'win');
$bw['mac'] = stristr($HTTP_USER_AGENT, 'mac');
$bw['linux'] = stristr($HTTP_USER_AGENT, 'linux');
$bw['unix'] = stristr($HTTP_USER_AGENT, 'unix');
$bw['ns4'] = stristr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
$bw['ns'] = ($bw['ns4'] || stristr($HTTP_USER_AGENT, 'netscape'));
$bw['ie'] = stristr($HTTP_USER_AGENT, 'msie');
$bw['mz'] = stristr($HTTP_USER_AGENT, 'mozilla/5');
$bw['opera'] = stristr($HTTP_USER_AGENT, 'opera');
$bw['safari'] = stristr($HTTP_USER_AGENT, 'safari');
if($bw['ns'])
{
$test = eregi("mozilla\/([0-9\.]+)", $HTTP_USER_AGENT, $regs);
$bw['ver'] = $test ? (float)$regs[1] : 0;
}
if($bw['mz'])
{
$test = ereg("rv:([0-9\.]+)", $HTTP_USER_AGENT, $regs);
$bw['ver'] = $test ? (float)$regs[1] : 0;
}
if($bw['ie'])
{
$test = eregi("msie ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
$bw['ver'] = $test ? (float)$regs[1] : 0;
}
if($bw['opera'])
{
$test = eregi("opera ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
$bw['ver'] = $test ? (float)$regs[1] : 0;
}
if(eregi(" ([a-z]{2})-([a-z]{2})", $HTTP_USER_AGENT, $regs))
$bw['lang'] = $regs[1];
else
$bw['lang'] = 'en';
$bw['dom'] = ($bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5) || ($bw['opera'] && $bw['ver']>=7));
$bw['pngalpha'] = $bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5.5) ||
($bw['ie'] && $bw['ver']>=5 && $bw['mac']) || ($bw['opera'] && $bw['ver']>=7) ? TRUE : FALSE;
return $bw;
}
// get text in the desired language from the language file
function rcube_label($attrib)
{
global $sess_user_lang, $INSTALL_PATH, $OUTPUT;
static $sa_text_data, $s_language, $utf8_decode;
// extract attributes
if (is_string($attrib))
$attrib = array('name' => $attrib);
$nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
$vars = isset($attrib['vars']) ? $attrib['vars'] : '';
$command_name = !empty($attrib['command']) ? $attrib['command'] : NULL;
$alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
// load localized texts
if (!$sa_text_data || $s_language != $sess_user_lang)
{
$sa_text_data = array();
// get english labels (these should be complete)
@include($INSTALL_PATH.'program/localization/en_US/labels.inc');
@include($INSTALL_PATH.'program/localization/en_US/messages.inc');
if (is_array($labels))
$sa_text_data = $labels;
if (is_array($messages))
$sa_text_data = array_merge($sa_text_data, $messages);
// include user language files
if ($sess_user_lang!='en' && is_dir($INSTALL_PATH.'program/localization/'.$sess_user_lang))
{
include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/labels.inc');
include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/messages.inc');
if (is_array($labels))
$sa_text_data = array_merge($sa_text_data, $labels);
if (is_array($messages))
$sa_text_data = array_merge($sa_text_data, $messages);
}
$s_language = $sess_user_lang;
}
// text does not exist
if (!($text_item = $sa_text_data[$alias]))
{
/*
raise_error(array('code' => 500,
'type' => 'php',
'line' => __LINE__,
'file' => __FILE__,
'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
*/
return "[$alias]";
}
// make text item array
$a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
// decide which text to use
if ($nr==1)
$text = $a_text_item['single'];
else if ($nr>0)
$text = $a_text_item['multiple'];
else if ($nr==0)
{
if ($a_text_item['none'])
$text = $a_text_item['none'];
else if ($a_text_item['single'])
$text = $a_text_item['single'];
else if ($a_text_item['multiple'])
$text = $a_text_item['multiple'];
}
// default text is single
if ($text=='')
$text = $a_text_item['single'];
// replace vars in text
if (is_array($attrib['vars']))
{
foreach ($attrib['vars'] as $var_key=>$var_value)
$a_replace_vars[substr($var_key, 0, 1)=='$' ? substr($var_key, 1) : $var_key] = $var_value;
}
if ($a_replace_vars)
$text = preg_replace('/\${?([_a-z]{1}[_a-z0-9]*)}?/ei', '$a_replace_vars["\1"]', $text);
// remove variables in text which were not available in arg $vars and $nr
eval("\$text = <<<EOF
$text
EOF;
");
// format output
if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
return ucfirst($text);
else if ($attrib['uppercase'])
return strtoupper($text);
else if ($attrib['lowercase'])
return strtolower($text);
return $text;
}
// send HTTP header for no-cacheing steps
function send_nocacheing_headers()
{
if (headers_sent())
return;
header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
header("Pragma: no-cache");
}
// send header with expire date 30 days in future
function send_future_expire_header()
{
if (headers_sent())
return;
header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+2600000)." GMT");
header("Cache-Control: ");
header("Pragma: ");
}
// function to convert an array to a javascript array
function array2js($arr, $type='')
{
if (!$type)
$type = 'mixed';
if (is_array($arr))
{
// no items in array
if (!sizeof($arr))
return 'new Array()';
else
{
$a_pairs = array();
$keys_arr = array_keys($arr);
$is_assoc = $have_numeric = 0;
for ($i=0; $i<sizeof($keys_arr); ++$i)
{
if(is_numeric($keys_arr[$i]))
$have_numeric = 1;
if (!is_numeric($keys_arr[$i]) || $keys_arr[$i]!=$i)
$is_assoc = 1;
if($is_assoc && $have_numeric)
break;
}
$previous_was_array = false;
while (list($key, $value) = each($arr))
{
// enclose key with quotes if it is not variable-name conform
if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */)
$key = "'$key'";
if (!is_array($value) && is_string($value))
{
$value = str_replace("\r\n", '\n', $value);
$value = str_replace("\n", '\n', $value);
}
$is_string = false;
if (!is_array($value))
{
if ($type=='string')
$is_string = true;
else if (($type == 'mixed' && is_bool($value)) || $type == 'bool')
{
$is_string = false;
$value = $value ? "true" : "false";
}
else if ((($type=='mixed' && is_numeric($value)) || $type=='int') && rc_strlen($value)<16) // js interprets numbers with digits >15 as ...e+...
$is_string = FALSE;
else
$is_string = TRUE;
}
if ($is_string)
$value = "'".preg_replace("/(?<!\\\)'/", "\'", $value)."'";
$a_pairs[] = sprintf("%s%s",
$is_assoc ? "$key:" : '',
is_array($value) ? array2js($value, $type) : $value);
}
if ($a_pairs)
{
if ($is_assoc)
$return = '{'.implode(',', $a_pairs).'}';
else
$return = '['.implode(',', $a_pairs).']';
}
return $return;
}
}
else
{
return $arr;
}
}
// similar function as in_array() ut case-insensitive
function in_array_nocase($needle, $haystack)
{
foreach ($haystack as $value)
{
if (strtolower($needle)===strtolower($value))
return TRUE;
}
return FALSE;
}
// find out if the string content means TRUE or FALSE
function get_boolean($str)
{
$str = strtolower($str);
if(in_array($str, array('false', '0', 'no', 'nein', ''), TRUE))
return FALSE;
else
return TRUE;
}
// parse a human readable string for a number of bytes
function parse_bytes($str)
{
if (is_numeric($str))
return intval($str);
if (preg_match('/([0-9]+)([a-z])/i', $str, $regs))
{
$bytes = floatval($regs[1]);
switch (strtolower($regs[2]))
{
case 'g':
$bytes *= 1073741824;
break;
case 'm':
$bytes *= 1048576;
break;
case 'k':
$bytes *= 1024;
break;
}
}
return intval($bytes);
}
// create a human readable string for a number of bytes
function show_bytes($bytes)
{
if ($bytes > 1073741824)
{
$gb = $bytes/1073741824;
$str = sprintf($gb>=10 ? "%d GB" : "%.1f GB", $gb);
}
else if ($bytes > 1048576)
{
$mb = $bytes/1048576;
$str = sprintf($mb>=10 ? "%d MB" : "%.1f MB", $mb);
}
else if ($bytes > 1024)
$str = sprintf("%d KB", round($bytes/1024));
else
$str = sprintf('%d B', $bytes);
return $str;
}
// convert paths like ../xxx to an absolute path using a base url
function make_absolute_url($path, $base_url)
{
$host_url = $base_url;
$abs_path = $path;
// cut base_url to the last directory
if (strpos($base_url, '/')>7)
{
$host_url = substr($base_url, 0, strpos($base_url, '/'));
$base_url = substr($base_url, 0, strrpos($base_url, '/'));
}
// $path is absolute
if ($path{0}=='/')
$abs_path = $host_url.$path;
else
{
// strip './' because its the same as ''
$path = preg_replace('/^\.\//', '', $path);
if(preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER))
foreach($matches as $a_match)
{
if (strrpos($base_url, '/'))
$base_url = substr($base_url, 0, strrpos($base_url, '/'));
$path = substr($path, 3);
}
$abs_path = $base_url.'/'.$path;
}
return $abs_path;
}
// wrapper function for strlen
function rc_strlen($str)
{
if (function_exists('mb_strlen'))
return mb_strlen($str);
else
return strlen($str);
}
// wrapper function for strtolower
function rc_strtolower($str)
{
if (function_exists('mb_strtolower'))
return mb_strtolower($str);
else
return strtolower($str);
}
// wrapper function for substr
function rc_substr($str, $start, $len)
{
if (function_exists('mb_substr'))
return mb_substr($str, $start, $len);
else
return substr($str, $start, $len);
}
// wrapper function for strpos
function rc_strpos($haystack, $needle, $offset=0)
{
if (function_exists('mb_strpos'))
return mb_strpos($haystack, $needle, $offset);
else
return strpos($haystack, $needle, $offset);
}
// wrapper function for strrpos
function rc_strrpos($haystack, $needle, $offset=0)
{
if (function_exists('mb_strrpos'))
return mb_strrpos($haystack, $needle, $offset);
else
return strrpos($haystack, $needle, $offset);
}
// replace the middle part of a string with ...
// if it is longer than the allowed length
function abbrevate_string($str, $maxlength, $place_holder='...')
{
$length = rc_strlen($str);
$first_part_length = floor($maxlength/2) - rc_strlen($place_holder);
if ($length > $maxlength)
{
$second_starting_location = $length - $maxlength + $first_part_length + 1;
$str = rc_substr($str, 0, $first_part_length) . $place_holder . rc_substr($str, $second_starting_location, $length);
}
return $str;
}
// make sure the string ends with a slash
function slashify($str)
{
return unslashify($str).'/';
}
// remove slash at the end of the string
function unslashify($str)
{
return preg_replace('/\/$/', '', $str);
}
// delete all files within a folder
function clear_directory($dir_path)
{
$dir = @opendir($dir_path);
if(!$dir) return FALSE;
while ($file = readdir($dir))
if (strlen($file)>2)
unlink("$dir_path/$file");
closedir($dir);
return TRUE;
}
// create a unix timestamp with a specified offset from now
function get_offset_time($offset_str, $factor=1)
{
if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs))
{
$amount = (int)$regs[1];
$unit = strtolower($regs[2]);
}
else
{
$amount = (int)$offset_str;
$unit = 's';
}
$ts = mktime();
switch ($unit)
{
case 'w':
$amount *= 7;
case 'd':
$amount *= 24;
case 'h':
$amount *= 60;
case 'h':
$amount *= 60;
case 's':
$ts += $amount * $factor;
}
return $ts;
}
/**
* strrstr
*
* return the last occurence of a string in another string
* @param haystack string string in which to search
* @param needle string string for which to search
* @return index of needle within haystack, or false if not found
*/
function strrstr($haystack, $needle)
{
$pver = phpversion();
if ($pver[0] >= 5)
{
return strrpos($haystack, $needle);
}
else
{
$index = strpos(strrev($haystack), strrev($needle));
if($index === false) {
return false;
}
$index = strlen($haystack) - strlen($needle) - $index;
return $index;
}
}
?>
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 211f9fbe8..1c2639d9b 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -1,910 +1,910 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/mail/compose.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Compose a new mail message with all headers and attachments |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
require_once('Mail/mimeDecode.php');
require_once('lib/html2text.inc');
// define constants for message compose mode
define('RCUBE_COMPOSE_REPLY', 0x0106);
define('RCUBE_COMPOSE_FORWARD', 0x0107);
define('RCUBE_COMPOSE_DRAFT', 0x0108);
// remove an attachment
if ($_action=='remove-attachment' && preg_match('/^rcmfile([0-9]+)$/', $_GET['_file'], $regs))
{
$id = $regs[1];
if (is_array($_SESSION['compose']['attachments'][$id]))
{
@unlink($_SESSION['compose']['attachments'][$id]['path']);
$_SESSION['compose']['attachments'][$id] = NULL;
$commands = sprintf("parent.%s.remove_from_attachment_list('rcmfile%d');\n", $JS_OBJECT_NAME, $id);
rcube_remote_response($commands);
exit;
}
}
$MESSAGE_FORM = NULL;
$MESSAGE = NULL;
// Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
// if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
// Since there are many ways to leave the compose page improperly, it seems necessary to clean-up an old
// compose when a "new/forward/reply/draft" is called - otherwise the old session attachments will appear
if (!is_array($_SESSION['compose']) || $_SESSION['compose']['id'] != get_input_value('_id', RCUBE_INPUT_GET))
{
rcmail_compose_cleanup();
$_SESSION['compose'] = array('id' => uniqid(rand()));
}
// add some labels to client
rcube_add_label('nosubject', 'norecipientwarning', 'nosubjectwarning', 'nobodywarning', 'notsentwarning', 'savingmessage', 'sendingmessage', 'messagesaved', 'converting');
// add config parameter to client script
$OUTPUT->add_script(sprintf("%s.set_env('draft_autosave', %d);", $JS_OBJECT_NAME, !empty($CONFIG['drafts_mbox']) ? $CONFIG['draft_autosave'] : 0));
// get reference message and set compose mode
if ($msg_uid = get_input_value('_reply_uid', RCUBE_INPUT_GET))
$compose_mode = RCUBE_COMPOSE_REPLY;
else if ($msg_uid = get_input_value('_forward_uid', RCUBE_INPUT_GET))
$compose_mode = RCUBE_COMPOSE_FORWARD;
else if ($msg_uid = get_input_value('_draft_uid', RCUBE_INPUT_GET))
$compose_mode = RCUBE_COMPOSE_DRAFT;
if (!empty($msg_uid))
{
// similar as in program/steps/mail/show.inc
$MESSAGE = array('UID' => $msg_uid);
$MESSAGE['headers'] = &$IMAP->get_headers($msg_uid);
$MESSAGE['structure'] = &$IMAP->get_structure($msg_uid);
$MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['headers']->subject);
$MESSAGE['parts'] = $IMAP->get_mime_numbers($MESSAGE['structure']);
if ($compose_mode == RCUBE_COMPOSE_REPLY)
{
$_SESSION['compose']['reply_uid'] = $msg_uid;
$_SESSION['compose']['reply_msgid'] = $MESSAGE['headers']->messageID;
$_SESSION['compose']['references'] = $MESSAGE['headers']->reference;
$_SESSION['compose']['references'] .= !empty($MESSAGE['headers']->reference) ? ' ' : '';
$_SESSION['compose']['references'] .= $MESSAGE['headers']->messageID;
if (!empty($_GET['_all']))
$MESSAGE['reply_all'] = 1;
}
else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
{
$_SESSION['compose']['forward_uid'] = $msg_uid;
}
else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
{
$_SESSION['compose']['draft_uid'] = $msg_uid;
}
}
/****** compose mode functions ********/
function rcmail_compose_headers($attrib)
{
global $IMAP, $MESSAGE, $DB, $compose_mode;
static $sa_recipients = array();
list($form_start, $form_end) = get_form_tags($attrib);
$out = '';
$part = strtolower($attrib['part']);
switch ($part)
{
case 'from':
return rcmail_compose_header_from($attrib);
case 'to':
$fname = '_to';
$header = 'to';
// we have contact id's as get parameters
if (!empty($_GET['_to']) && preg_match('/^[0-9]+(,[0-9]+)*$/', $_GET['_to']))
{
$a_recipients = array();
$sql_result = $DB->query("SELECT name, email
FROM ".get_table_name('contacts')."
WHERE user_id=?
AND del<>1
AND contact_id IN (".$_GET['_to'].")",
$_SESSION['user_id']);
while ($sql_arr = $DB->fetch_assoc($sql_result))
$a_recipients[] = format_email_recipient($sql_arr['email'], $sql_arr['name']);
if (sizeof($a_recipients))
$fvalue = join(', ', $a_recipients);
}
else if (!empty($_GET['_to']))
$fvalue = get_input_value('_to', RCUBE_INPUT_GET);
case 'cc':
if (!$fname)
{
$fname = '_cc';
$header = 'cc';
}
case 'bcc':
if (!$fname)
{
$fname = '_bcc';
$header = 'bcc';
}
$allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'wrap', 'tabindex');
$field_type = 'textarea';
break;
case 'replyto':
case 'reply-to':
$fname = '_replyto';
$allow_attrib = array('id', 'class', 'style', 'size', 'tabindex');
$field_type = 'textfield';
break;
}
if ($fname && !empty($_POST[$fname]))
$fvalue = get_input_value($fname, RCUBE_INPUT_POST, TRUE);
else if ($header && $compose_mode == RCUBE_COMPOSE_REPLY)
{
// get recipent address(es) out of the message headers
if ($header=='to' && !empty($MESSAGE['headers']->replyto))
$fvalue = $MESSAGE['headers']->replyto;
else if ($header=='to' && !empty($MESSAGE['headers']->from))
$fvalue = $MESSAGE['headers']->from;
// add recipent of original message if reply to all
else if ($header=='cc' && !empty($MESSAGE['reply_all']))
{
if ($v = $MESSAGE['headers']->to)
$fvalue .= $v;
if ($v = $MESSAGE['headers']->cc)
$fvalue .= (!empty($fvalue) ? ', ' : '') . $v;
}
// split recipients and put them back together in a unique way
if (!empty($fvalue))
{
$to_addresses = $IMAP->decode_address_list($fvalue);
$fvalue = '';
foreach ($to_addresses as $addr_part)
{
if (!in_array($addr_part['mailto'], $sa_recipients) && (!$MESSAGE['FROM'] || !in_array($addr_part['mailto'], $MESSAGE['FROM'])))
{
$fvalue .= (strlen($fvalue) ? ', ':'').$addr_part['string'];
$sa_recipients[] = $addr_part['mailto'];
}
}
}
}
else if ($header && $compose_mode == RCUBE_COMPOSE_DRAFT)
{
// get drafted headers
if ($header=='to' && !empty($MESSAGE['headers']->to))
$fvalue = $IMAP->decode_header($MESSAGE['headers']->to);
if ($header=='cc' && !empty($MESSAGE['headers']->cc))
$fvalue = $IMAP->decode_header($MESSAGE['headers']->cc);
if ($header=='bcc' && !empty($MESSAGE['headers']->bcc))
$fvalue = $IMAP->decode_header($MESSAGE['headers']->bcc);
}
if ($fname && $field_type)
{
// pass the following attributes to the form class
$field_attrib = array('name' => $fname);
foreach ($attrib as $attr => $value)
if (in_array($attr, $allow_attrib))
$field_attrib[$attr] = $value;
// create teaxtarea object
$input = new $field_type($field_attrib);
$out = $input->show($fvalue);
}
if ($form_start)
$out = $form_start.$out;
return $out;
}
function rcmail_compose_header_from($attrib)
{
global $IMAP, $MESSAGE, $DB, $OUTPUT, $JS_OBJECT_NAME, $compose_mode;
// pass the following attributes to the form class
$field_attrib = array('name' => '_from');
foreach ($attrib as $attr => $value)
if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
$field_attrib[$attr] = $value;
// extract all recipients of the reply-message
$a_recipients = array();
if ($compose_mode == RCUBE_COMPOSE_REPLY && is_object($MESSAGE['headers']))
{
$MESSAGE['FROM'] = array();
$a_to = $IMAP->decode_address_list($MESSAGE['headers']->to);
foreach ($a_to as $addr)
{
if (!empty($addr['mailto']))
$a_recipients[] = $addr['mailto'];
}
if (!empty($MESSAGE['headers']->cc))
{
$a_cc = $IMAP->decode_address_list($MESSAGE['headers']->cc);
foreach ($a_cc as $addr)
{
if (!empty($addr['mailto']))
$a_recipients[] = $addr['mailto'];
}
}
}
// get this user's identities
$sql_result = $DB->query("SELECT identity_id, name, email, signature, html_signature
FROM ".get_table_name('identities')."
WHERE user_id=?
AND del<>1
ORDER BY ".$DB->quoteIdentifier('standard')." DESC, name ASC",
$_SESSION['user_id']);
if ($DB->num_rows($sql_result))
{
$from_id = 0;
$a_signatures = array();
$field_attrib['onchange'] = "$JS_OBJECT_NAME.change_identity(this)";
$select_from = new select($field_attrib);
while ($sql_arr = $DB->fetch_assoc($sql_result))
{
$identity_id = $sql_arr['identity_id'];
$select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
// add signature to array
if (!empty($sql_arr['signature']))
{
$a_signatures[$identity_id]['text'] = $sql_arr['signature'];
$a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false;
if ($a_signatures[$identity_id]['is_html'])
{
$h2t = new html2text($a_signatures[$identity_id]['text'], false, false);
$plainTextPart = $h2t->get_text();
$a_signatures[$identity_id]['plain_text'] = trim($plainTextPart);
}
}
// set identity if it's one of the reply-message recipients
if (in_array($sql_arr['email'], $a_recipients))
$from_id = $sql_arr['identity_id'];
if ($compose_mode == RCUBE_COMPOSE_REPLY && is_array($MESSAGE['FROM']))
$MESSAGE['FROM'][] = $sql_arr['email'];
if ($compose_mode == RCUBE_COMPOSE_DRAFT && strstr($MESSAGE['headers']->from, $sql_arr['email']))
$from_id = $sql_arr['identity_id'];
}
// overwrite identity selection with post parameter
if (isset($_POST['_from']))
$from_id = get_input_value('_from', RCUBE_INPUT_POST);
$out = $select_from->show($from_id);
// add signatures to client
$OUTPUT->add_script(sprintf("%s.set_env('signatures', %s);", $JS_OBJECT_NAME, array2js($a_signatures)));
}
else
{
$input_from = new textfield($field_attrib);
$out = $input_from->show($_POST['_from']);
}
if ($form_start)
$out = $form_start.$out;
return $out;
}
function rcmail_compose_body($attrib)
{
global $CONFIG, $OUTPUT, $MESSAGE, $JS_OBJECT_NAME, $compose_mode;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
if (empty($attrib['id']))
$attrib['id'] = 'rcmComposeMessage';
$attrib['name'] = '_message';
if ($CONFIG['htmleditor'])
$isHtml = true;
else
$isHtml = false;
$body = '';
// use posted message body
if (!empty($_POST['_message']))
{
$body = get_input_value('_message', RCUBE_INPUT_POST, TRUE);
}
// compose reply-body
else if ($compose_mode == RCUBE_COMPOSE_REPLY)
{
$hasHtml = rcmail_has_html_part($MESSAGE['parts']);
if ($hasHtml && $CONFIG['htmleditor'])
{
$body = rcmail_first_html_part($MESSAGE);
$isHtml = true;
}
else
{
$body = rcmail_first_text_part($MESSAGE);
$isHtml = false;
}
if (strlen($body))
$body = rcmail_create_reply_body($body, $isHtml);
}
// forward message body inline
else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
{
$hasHtml = rcmail_has_html_part($MESSAGE['parts']);
if ($hasHtml && $CONFIG['htmleditor'])
{
$body = rcmail_first_html_part($MESSAGE);
$isHtml = true;
}
else
{
$body = rcmail_first_text_part($MESSAGE);
$isHtml = false;
}
if (strlen($body))
$body = rcmail_create_forward_body($body, $isHtml);
}
else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
{
$hasHtml = rcmail_has_html_part($MESSAGE['parts']);
if ($hasHtml && $CONFIG['htmleditor'])
{
$body = rcmail_first_html_part($MESSAGE);
$isHtml = true;
}
else
{
$body = rcmail_first_text_part($MESSAGE);
$isHtml = false;
}
if (strlen($body))
$body = rcmail_create_draft_body($body, $isHtml);
}
$OUTPUT->include_script('tiny_mce/tiny_mce.js');
$OUTPUT->include_script("editor.js");
$OUTPUT->add_script('rcmail_editor_init("$__skin_path");');
$out = $form_start ? "$form_start\n" : '';
$saveid = new hiddenfield(array('name' => '_draft_saveid', 'value' => $compose_mode==RCUBE_COMPOSE_DRAFT ? str_replace(array('<','>'), "", $MESSAGE['headers']->messageID) : ''));
$out .= $saveid->show();
$drafttoggle = new hiddenfield(array('name' => '_draft', 'value' => 'yes'));
$out .= $drafttoggle->show();
$msgtype = new hiddenfield(array('name' => '_is_html', 'value' => ($isHtml?"1":"0")));
$out .= $msgtype->show();
// If desired, set this text area to be editable by TinyMCE
if ($isHtml)
$attrib['mce_editable'] = "true";
$textarea = new textarea($attrib);
$out .= $textarea->show($body);
$out .= $form_end ? "\n$form_end" : '';
// include GoogieSpell
if (!empty($CONFIG['enable_spellcheck']) && !$isHtml)
{
$lang_set = '';
if (!empty($CONFIG['spellcheck_languages']) && is_array($CONFIG['spellcheck_languages']))
$lang_set = "googie.setLanguages(".array2js($CONFIG['spellcheck_languages']).");\n";
$OUTPUT->include_script('googiespell.js');
$OUTPUT->add_script(sprintf(
"var googie = new GoogieSpell('\$__skin_path/images/googiespell/','%s&_action=spell&lang=');\n".
"googie.lang_chck_spell = \"%s\";\n".
"googie.lang_rsm_edt = \"%s\";\n".
"googie.lang_close = \"%s\";\n".
"googie.lang_revert = \"%s\";\n".
"googie.lang_no_error_found = \"%s\";\n%s".
"googie.setCurrentLanguage('%s');\n".
"googie.decorateTextarea('%s');\n".
"%s.set_env('spellcheck', googie);",
$GLOBALS['COMM_PATH'],
JQ(Q(rcube_label('checkspelling'))),
JQ(Q(rcube_label('resumeediting'))),
JQ(Q(rcube_label('close'))),
JQ(Q(rcube_label('revertto'))),
JQ(Q(rcube_label('nospellerrors'))),
$lang_set,
substr($_SESSION['user_lang'], 0, 2),
$attrib['id'],
$JS_OBJECT_NAME), 'foot');
rcube_add_label('checking');
}
$out .= "\n".'<iframe name="savetarget" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
return $out;
}
function rcmail_create_reply_body($body, $bodyIsHtml)
{
global $IMAP, $MESSAGE;
if (! $bodyIsHtml)
{
// soft-wrap message first
$body = wordwrap($body, 75);
// split body into single lines
$a_lines = preg_split('/\r?\n/', $body);
// add > to each line
for($n=0; $n<sizeof($a_lines); $n++)
{
if (strpos($a_lines[$n], '>')===0)
$a_lines[$n] = '>'.$a_lines[$n];
else
$a_lines[$n] = '> '.$a_lines[$n];
}
$body = join("\n", $a_lines);
// add title line
$prefix = sprintf("\n\n\nOn %s, %s wrote:\n",
$MESSAGE['headers']->date,
$IMAP->decode_header($MESSAGE['headers']->from));
// try to remove the signature
if ($sp = strrstr($body, '-- '))
{
if ($body{$sp+3}==' ' || $body{$sp+3}=="\n" || $body{$sp+3}=="\r")
$body = substr($body, 0, $sp-1);
}
$suffix = '';
}
else
{
$prefix = sprintf("<br><br>On %s, %s wrote:<br><blockquote type=\"cite\" " .
"style=\"padding-left: 5px; border-left: #1010ff 2px solid; " .
"margin-left: 5px; width: 100%%\">",
$MESSAGE['headers']->date,
$IMAP->decode_header($MESSAGE['headers']->from));
$suffix = "</blockquote>";
}
return $prefix.$body.$suffix;
}
function rcmail_create_forward_body($body, $bodyIsHtml)
{
global $IMAP, $MESSAGE;
if (! $bodyIsHtml)
{
// soft-wrap message first
$body = wordwrap($body, 80);
$prefix = sprintf("\n\n\n-------- Original Message --------\nSubject: %s\nDate: %s\nFrom: %s\nTo: %s\n\n",
$MESSAGE['subject'],
$MESSAGE['headers']->date,
$IMAP->decode_header($MESSAGE['headers']->from),
$IMAP->decode_header($MESSAGE['headers']->to));
}
else
{
$prefix = sprintf(
"<br><br>-------- Original Message --------" .
"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Subject: </th><td>%s</td></tr>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Date: </th><td>%s</td></tr>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">From: </th><td>%s</td></tr>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">To: </th><td>%s</td></tr>" .
"</tbody></table><br>",
Q($MESSAGE['subject']),
Q($MESSAGE['headers']->date),
Q($IMAP->decode_header($MESSAGE['headers']->from)),
Q($IMAP->decode_header($MESSAGE['headers']->to)));
}
// add attachments
if (!isset($_SESSION['compose']['forward_attachments']) &&
is_array($MESSAGE['parts']) && sizeof($MESSAGE['parts'])>1)
rcmail_write_compose_attachments($MESSAGE);
return $prefix.$body;
}
function rcmail_create_draft_body($body, $bodyIsHtml)
{
global $IMAP, $MESSAGE;
// add attachments
if (!isset($_SESSION['compose']['forward_attachments']) &&
is_array($MESSAGE['parts']) && sizeof($MESSAGE['parts'])>1)
rcmail_write_compose_attachments($MESSAGE);
return $body;
}
function rcmail_write_compose_attachments(&$message)
{
global $IMAP, $CONFIG;
$temp_dir = unslashify($CONFIG['temp_dir']);
if (!is_array($_SESSION['compose']['attachments']))
$_SESSION['compose']['attachments'] = array();
foreach ($message['parts'] as $pid => $part)
{
if ($part->ctype_primary != 'message' && $part->ctype_primary != 'text' &&
($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id'] ||
(empty($part->disposition) && ($part->d_parameters['filename'] || $part->ctype_parameters['name']))))
{
$tmp_path = tempnam($temp_dir, 'rcmAttmnt');
if ($fp = fopen($tmp_path, 'w'))
{
fwrite($fp, $IMAP->get_message_part($message['UID'], $pid, $part->encoding));
fclose($fp);
$filename = !empty($part->d_parameters['filename']) ? $part->d_parameters['filename'] :
(!empty($part->ctype_parameters['name']) ? $part->ctype_parameters['name'] :
(!empty($part->headers['content-description']) ? $part->headers['content-description'] : 'file'));
$_SESSION['compose']['attachments'][] = array(
'name' => rcube_imap::decode_mime_string($filename),
'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
'path' => $tmp_path
);
}
}
}
$_SESSION['compose']['forward_attachments'] = TRUE;
}
function rcmail_compose_subject($attrib)
{
global $CONFIG, $MESSAGE, $compose_mode;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
$attrib['name'] = '_subject';
$textfield = new textfield($attrib);
$subject = '';
// use subject from post
if (isset($_POST['_subject']))
$subject = get_input_value('_subject', RCUBE_INPUT_POST, TRUE);
// create a reply-subject
else if ($compose_mode == RCUBE_COMPOSE_REPLY)
{
if (eregi('^re:', $MESSAGE['subject']))
$subject = $MESSAGE['subject'];
else
$subject = 'Re: '.$MESSAGE['subject'];
}
// create a forward-subject
else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
{
if (eregi('^fwd:', $MESSAGE['subject']))
$subject = $MESSAGE['subject'];
else
$subject = 'Fwd: '.$MESSAGE['subject'];
}
// creeate a draft-subject
else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
$subject = $MESSAGE['subject'];
$out = $form_start ? "$form_start\n" : '';
$out .= $textfield->show($subject);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_compose_attachment_list($attrib)
{
global $OUTPUT, $JS_OBJECT_NAME, $CONFIG;
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmAttachmentList';
// allow the following attributes to be added to the <ul> tag
$attrib_str = create_attrib_string($attrib, array('id', 'class', 'style'));
$out = '<ul'. $attrib_str . ">\n";
if (is_array($_SESSION['compose']['attachments']))
{
if ($attrib['deleteicon'])
$button = sprintf('<img src="%s%s" alt="%s" border="0" style="padding-right:2px;vertical-align:middle" />',
$CONFIG['skin_path'],
$attrib['deleteicon'],
rcube_label('delete'));
else
$button = rcube_label('delete');
foreach ($_SESSION['compose']['attachments'] as $id => $a_prop)
$out .= sprintf('<li id="rcmfile%d"><a href="#delete" onclick="return %s.command(\'remove-attachment\',\'rcmfile%d\', this)" title="%s">%s</a>%s</li>',
$id,
$JS_OBJECT_NAME,
$id,
Q(rcube_label('delete')),
$button,
Q($a_prop['name']));
}
$OUTPUT->add_script(sprintf("%s.gui_object('attachmentlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
$out .= '</ul>';
return $out;
}
function rcmail_compose_attachment_form($attrib)
{
global $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD;
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmUploadbox';
// allow the following attributes to be added to the <div> tag
$attrib_str = create_attrib_string($attrib, array('id', 'class', 'style'));
$input_field = rcmail_compose_attachment_field(array());
$label_send = rcube_label('upload');
$label_close = rcube_label('close');
$out = <<<EOF
<div$attrib_str>
<form action="./" method="post" enctype="multipart/form-data">
$SESS_HIDDEN_FIELD
$input_field<br />
<input type="button" value="$label_close" class="button" onclick="document.getElementById('$attrib[id]').style.visibility='hidden'" />
<input type="button" value="$label_send" class="button" onclick="$JS_OBJECT_NAME.command('send-attachment', this.form)" />
</form>
</div>
EOF;
$OUTPUT->add_script(sprintf("%s.gui_object('uploadbox', '%s');", $JS_OBJECT_NAME, $attrib['id']));
return $out;
}
function rcmail_compose_attachment_field($attrib)
{
// allow the following attributes to be added to the <input> tag
$attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'size'));
$out = '<input type="file" name="_attachments[]"'. $attrib_str . " />";
return $out;
}
function rcmail_priority_selector($attrib)
{
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
$attrib['name'] = '_priority';
$selector = new select($attrib);
$selector->add(array(rcube_label('lowest'),
rcube_label('low'),
rcube_label('normal'),
rcube_label('high'),
rcube_label('highest')),
array(5, 4, 0, 2, 1));
- $sel = isset($_POST['_priority']) ? $_POST['_priority'] : rcube_label('normal');
+ $sel = isset($_POST['_priority']) ? $_POST['_priority'] : 0;
$out = $form_start ? "$form_start\n" : '';
$out .= $selector->show($sel);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_receipt_checkbox($attrib)
{
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
if (!isset($attrib['id']))
$attrib['id'] = 'receipt';
$attrib['name'] = '_receipt';
$attrib['value'] = '1';
$checkbox = new checkbox($attrib);
$out = $form_start ? "$form_start\n" : '';
$out .= $checkbox->show(0);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_editor_selector($attrib)
{
global $CONFIG, $MESSAGE, $compose_mode;
$choices = array(
'html' => 'htmltoggle',
'plain' => 'plaintoggle'
);
// determine whether HTML or plain text should be checked
if ($CONFIG['htmleditor'])
$useHtml = true;
else
$useHtml = false;
if ($compose_mode == RCUBE_COMPOSE_REPLY ||
$compose_mode == RCUBE_COMPOSE_FORWARD ||
$compose_mode == RCUBE_COMPOSE_DRAFT)
{
$hasHtml = rcmail_has_html_part($MESSAGE['parts']);
$useHtml = ($hasHtml && $CONFIG['htmleditor']);
}
$selector = '';
$attrib['name'] = '_editorSelect';
$attrib['onchange'] = 'return rcmail_toggle_editor(this)';
foreach ($choices as $value => $text)
{
$checked = '';
if ((($value == 'html') && $useHtml) ||
(($value != 'html') && !$useHtml))
$attrib['checked'] = 'true';
else
unset($attrib['checked']);
$attrib['id'] = '_' . $value;
$rb = new radiobutton($attrib);
$selector .= sprintf("<td>%s</td><td class=\"title\"><label for=\"%s\">%s</label></td>",
$rb->show($value),
$attrib['id'],
rcube_label($text));
}
return $selector;
}
function get_form_tags($attrib)
{
global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $MESSAGE_FORM, $SESS_HIDDEN_FIELD;
$form_start = '';
if (!strlen($MESSAGE_FORM))
{
$hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task']));
$hiddenfields->add(array('name' => '_action', 'value' => 'send'));
$form_start = empty($attrib['form']) ? '<form name="form" action="./" method="post">' : '';
$form_start .= "\n$SESS_HIDDEN_FIELD\n";
$form_start .= $hiddenfields->show();
}
$form_end = (strlen($MESSAGE_FORM) && !strlen($attrib['form'])) ? '</form>' : '';
$form_name = !empty($attrib['form']) ? $attrib['form'] : 'form';
if (!strlen($MESSAGE_FORM))
$OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('messageform', '$form_name');");
$MESSAGE_FORM = $form_name;
return array($form_start, $form_end);
}
function format_email_recipient($email, $name='')
{
if ($name && $name != $email)
return sprintf('%s <%s>', strpos($name, ",") ? '"'.$name.'"' : $name, $email);
else
return $email;
}
function rcmail_charset_pulldown($selected='ISO-8859-1')
{
$select = new select();
return $select->show($selected);
}
/****** get contacts for this user and add them to client scripts ********/
$sql_result = $DB->query("SELECT name, email
FROM ".get_table_name('contacts')." WHERE user_id=?
AND del<>1",$_SESSION['user_id']);
if ($DB->num_rows($sql_result))
{
$a_contacts = array();
while ($sql_arr = $DB->fetch_assoc($sql_result))
if ($sql_arr['email'])
$a_contacts[] = format_email_recipient($sql_arr['email'], JQ($sql_arr['name']));
$OUTPUT->add_script(sprintf("$JS_OBJECT_NAME.set_env('contacts', %s);", array2js($a_contacts)));
}
parse_template('compose');
?>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Mar 19, 9:04 AM (23 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
457669
Default Alt Text
(90 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment