diff options
author | thomascube <thomas@roundcube.net> | 2005-09-25 14:18:03 +0000 |
---|---|---|
committer | thomascube <thomas@roundcube.net> | 2005-09-25 14:18:03 +0000 |
commit | 4e17e6c9dbac8991ee8b302cb2581241247dc8bc (patch) | |
tree | d877546f6bd334b041734498e81f6299e005b01c |
Initial revision
164 files changed, 20961 insertions, 0 deletions
diff --git a/.htaccess b/.htaccess new file mode 100644 index 000000000..c0ab7f0ae --- /dev/null +++ b/.htaccess @@ -0,0 +1,12 @@ +php_flag display_errors On +php_value session.gc_maxlifetime 21600 +php_value session.gc_divisor 500 +php_value upload_max_filesize 2m + +<FilesMatch "(\.inc|\~)$|^_"> + Order allow,deny + Deny from all +</FilesMatch> + +Order deny,allow +Allow from all diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000..1fa699292 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,26 @@ +CHANGELOG RoundCube Webmail +--------------------------- + +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/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/INSTALL b/INSTALL new file mode 100644 index 000000000..5317b80d5 --- /dev/null +++ b/INSTALL @@ -0,0 +1,32 @@ + +INSTALLATION +============ + +1. Decompress and put this folder somewhere inside your document root +2. Make shure that the following directories are writable by the webserver + - /temp + - /logs +3. Modify the files in /config to suit your local environment +4. Create database tables using the queries in file 'SQL/initial.sql' + Rename tables if you like, but make shure the names are also changed in /config/db.inc +5. Done! + + +REQUIREMENTS +============ + +* The Apache Webserver +* .htaccess support allowing overrides for DirectoryIndex +* PHP Version 4.3.1 or greater +* php.ini options: + - error_reporting E_ALL & ~E_NOTICE (or lower) + - file_uploads on (for attachment upload features) +* The MySQL database engine +* A database with permission to create tables + + +CONFIGURATION +============= + +Change the files in /config/ according your environment and you needs. +Details about the config paramaters can be found in the config files. diff --git a/SQL/initial.sql b/SQL/initial.sql new file mode 100644 index 000000000..64d650867 --- /dev/null +++ b/SQL/initial.sql @@ -0,0 +1,93 @@ +-- RoundCube Webmail initial database structure +-- Version 0.1a +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `cache` +-- + +CREATE TABLE `cache` ( + `cache_id` int(10) unsigned NOT NULL auto_increment, + `user_id` int(10) unsigned NOT NULL default '0', + `session_id` varchar(32) default NULL, + `cache_key` varchar(128) NOT NULL default '', + `created` datetime NOT NULL default '0000-00-00 00:00:00', + `data` longtext NOT NULL, + PRIMARY KEY (`cache_id`), + KEY `user_id` (`user_id`), + KEY `cache_key` (`cache_key`), + KEY `session_id` (`session_id`) +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `contacts` +-- + +CREATE TABLE `contacts` ( + `contact_id` int(10) unsigned NOT NULL auto_increment, + `user_id` int(10) unsigned NOT NULL default '0', + `del` enum('0','1') NOT NULL default '0', + `name` varchar(128) NOT NULL default '', + `email` varchar(128) NOT NULL default '', + `firstname` varchar(128) NOT NULL default '', + `surname` varchar(128) NOT NULL default '', + `vcard` text NOT NULL, + PRIMARY KEY (`contact_id`), + KEY `user_id` (`user_id`) +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `identities` +-- + +CREATE TABLE `identities` ( + `identity_id` int(10) unsigned NOT NULL auto_increment, + `user_id` int(10) unsigned NOT NULL default '0', + `del` enum('0','1') NOT NULL default '0', + `default` enum('0','1') NOT NULL default '0', + `name` varchar(128) NOT NULL default '', + `organization` varchar(128) NOT NULL default '', + `email` varchar(128) NOT NULL default '', + `reply-to` varchar(128) NOT NULL default '', + `bcc` varchar(128) NOT NULL default '', + `signature` text NOT NULL, + PRIMARY KEY (`identity_id`), + KEY `user_id` (`user_id`) +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `session` +-- + +CREATE TABLE `session` ( + `sess_id` varchar(32) NOT NULL default '', + `created` datetime NOT NULL default '0000-00-00 00:00:00', + `changed` datetime NOT NULL default '0000-00-00 00:00:00', + `vars` text NOT NULL, + PRIMARY KEY (`sess_id`) +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +CREATE TABLE `users` ( + `user_id` int(10) unsigned NOT NULL auto_increment, + `username` varchar(128) NOT NULL default '', + `mail_host` varchar(255) NOT NULL default '', + `created` datetime NOT NULL default '0000-00-00 00:00:00', + `last_login` datetime NOT NULL default '0000-00-00 00:00:00', + `language` varchar(5) NOT NULL default 'en', + `preferences` text NOT NULL, + PRIMARY KEY (`user_id`) +) TYPE=MyISAM; diff --git a/UPGRADING b/UPGRADING new file mode 100644 index 000000000..368f0480e --- /dev/null +++ b/UPGRADING @@ -0,0 +1,15 @@ +UPDATE instructions +=================== + +Follow these instructions if upgrading from a previous version +of RoundCube Webmail. + + + +from versions 0.1-alpha and 0.1-20050811 +---------------------------------------- +- replace all files in folder /program/ +- add these line to /config/main.inc.php + $rcmail_config['trash_mbox'] = 'Trash'; + $rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash'); + $rcmail_config['prefer_html'] = TRUE; diff --git a/config/.htaccess b/config/.htaccess new file mode 100644 index 000000000..8e6a345dc --- /dev/null +++ b/config/.htaccess @@ -0,0 +1,2 @@ +Order allow,deny +Deny from all
\ No newline at end of file diff --git a/config/db.inc.php b/config/db.inc.php new file mode 100644 index 000000000..fb6d2bb8b --- /dev/null +++ b/config/db.inc.php @@ -0,0 +1,46 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | Configuration file for database access | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + +-----------------------------------------------------------------------+ + +*/ + +$rcmail_config = array(); + +// database engine (currently supported: mysql) +$rcmail_config['db_type'] = 'mysql'; + +// database host +$rcmail_config['db_host'] = 'localhost'; + +// database user +$rcmail_config['db_user'] = 'roundcube'; + +// pwd +$rcmail_config['db_pass'] = 'pass'; + +// database name +$rcmail_config['db_name'] = 'roundcubemail'; + + +// you can define specific table names used to store webmail data +$rcmail_config['db_table_users'] = 'users'; + +$rcmail_config['db_table_identities'] = 'identities'; + +$rcmail_config['db_table_contacts'] = 'contacts'; + +$rcmail_config['db_table_session'] = 'session'; + +$rcmail_config['db_table_cache'] = 'cache'; + + +// end db config file +?> diff --git a/config/main.inc.php b/config/main.inc.php new file mode 100644 index 000000000..0e580318b --- /dev/null +++ b/config/main.inc.php @@ -0,0 +1,96 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | Main configuration file | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + +-----------------------------------------------------------------------+ + +*/ + +$rcmail_config = array(); + + +// system error reporting: 1 = log; 2 = report (not implemented yet), 4 = show +$rcmail_config['debug_level'] = 5; + +// automatically create a new user when log-in the first time +// set to false if only registered users can use this service +$rcmail_config['auto_create_user'] = TRUE; + +// the mail host chosen to perform the log-in +// leave blank to show a textbox at login, give a list of hosts +// to display a pulldown menu or set one host as string +$rcmail_config['default_host'] = ''; + +// use this host for sending mails. +// if left blank, the PHP mail() function is used +$rcmail_config['smtp_server'] = ''; + +// SMTP username (if required) +$rcmail_config['smtp_user'] = ''; + +// SMTP password (if required) +$rcmail_config['smtp_pass'] = ''; + +// Log sent messages +$rcmail_config['smtp_log'] = TRUE; + +// these cols are shown in the message list +// available cols are: subject, from, to, cc, replyto, date, size, encoding +$rcmail_config['list_cols'] = array('subject', 'from', 'date', 'size'); + +// relative path to the skin folder +$rcmail_config['skin_path'] = 'skins/default/'; + +// use this folder to store temp files (must be writebale for apache user) +$rcmail_config['temp_dir'] = 'temp/'; + +// check client IP in session athorization +$rcmail_config['ip_check'] = TRUE; + +// not shure what this was good for :-) +$rcmail_config['locale_string'] = 'de_DE'; + +// use this format for short date display +$rcmail_config['date_short'] = 'D H:i'; + +// use this format for detailed date/time formatting +$rcmail_config['date_long'] = 'd.m.Y H:i'; + +// add this user-agent to message headers when sending +$rcmail_config['useragent'] = 'RoundCube Webmail/0.1a'; + +// only list folders within this path +$rcmail_config['imap_root'] = ''; + +// store sent message is this mailbox +// leave blank if sent messages should not be stored +$rcmail_config['sent_mbox'] = 'Sent'; + +// move messages to this folder when deleting them +// leave blank if they should be deleted directly +$rcmail_config['trash_mbox'] = 'Trash'; + +// display these folders separately in the mailbox list +$rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash'); + + +/***** these settings can be overwritten by user's preferences *****/ + +// show up to X items in list view +$rcmail_config['pagesize'] = 40; + +// use this timezone to display date/time +$rcmail_config['timezone'] = 1; + +// prefer displaying HTML messages +$rcmail_config['prefer_html'] = TRUE; + + +// end of config file +?> diff --git a/index.php b/index.php new file mode 100644 index 000000000..19623aa30 --- /dev/null +++ b/index.php @@ -0,0 +1,273 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | RoundCube Webmail IMAP Client | + | Version 0.1-20050811 | + | | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the following conditions | + | are met: | + | | + | o Redistributions of source code must retain the above copyright | + | notice, this list of conditions and the following disclaimer. | + | o Redistributions in binary form must reproduce the above copyright | + | notice, this list of conditions and the following disclaimer in the | + | documentation and/or other materials provided with the distribution.| + | o The names of the authors may not be used to endorse or promote | + | products derived from this software without specific prior written | + | permission. | + | | + | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | + | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | + | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | + | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | + | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | + | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | + | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | + | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | + | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | + | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | + | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +// define global vars +$INSTALL_PATH = './'; +$OUTPUT_TYPE = 'html'; +$JS_OBJECT_NAME = 'rcmail'; + + +// set environment first +ini_set('include_path', ini_get('include_path').PATH_SEPARATOR.'program'.PATH_SEPARATOR.'program/lib'); +ini_set('session.name', 'sessid'); +ini_set('session.use_cookies', 1); +//ini_set('session.save_path', $INSTALL_PATH.'session'); + + +// increase maximum execution time for php scripts +set_time_limit('120'); + + +// include base files +require_once('include/rcube_shared.inc'); +require_once('include/rcube_imap.inc'); +require_once('include/rcube_mysql.inc'); +require_once('include/bugs.inc'); +require_once('include/main.inc'); +require_once('include/cache.inc'); + + +// catch some url/post parameters +$_auth = strlen($_POST['_auth']) ? $_POST['_auth'] : $_GET['_auth']; +$_task = strlen($_POST['_task']) ? $_POST['_task'] : ($_GET['_task'] ? $_GET['_task'] : 'mail'); +$_action = strlen($_POST['_action']) ? $_POST['_action'] : $_GET['_action']; +$_framed = ($_GET['_framed'] || $_POST['_framed']); + +// start session with requested task +rcmail_startup($_task); + + +// set session related variables +$COMM_PATH = sprintf('./?_auth=%s&_task=%s', $sess_auth, $_task); +$SESS_HIDDEN_FIELD = sprintf('<input type="hidden" name="_auth" value="%s" />', $sess_auth); + + +// add framed parameter +if ($_GET['_framed'] || $_POST['_framed']) + { + $COMM_PATH .= '&_framed=1'; + $SESS_HIDDEN_FIELD = "\n".'<input type="hidden" name="_framed" value="1" />'; + } + + +// init necessary objects for GUI +load_gui(); + + +// error steps +if ($_action=='error' && strlen($_GET['_code'])) + { + raise_error(array('code' => hexdec($_GET['_code'])), FALSE, TRUE); + } + + +// try to log in +if ($_action=='login' && $_task=='mail') + { + $host = $_POST['_host'] ? $_POST['_host'] : $CONFIG['default_host']; + + // check if client supports cookies + if (!$_COOKIE[session_name()]) + { + show_message("cookiesdisabled", 'warning'); + } + else if ($_POST['_user'] && $_POST['_pass'] && rcmail_login($_POST['_user'], $_POST['_pass'], $host)) + { + // send redirect + header("Location: $COMM_PATH"); + exit; + } + else + { + show_message("loginfailed", 'warning'); + $_SESSION['user_id'] = ''; + } + } + +// end session +else if ($_action=='logout' && $_SESSION['user_id']) + { + show_message('loggedout'); + rcmail_kill_session(); + } + +// check session cookie and auth string +else if ($_action!='login' && $_auth && $sess_auth) + { + if ($_auth !== $sess_auth || $_auth != rcmail_auth_hash($_SESSION['client_id'], $_SESSION['auth_time'])) + { + show_message('sessionerror', 'error'); + rcmail_kill_session(); + } + } + + +// log in to imap server +if ($_SESSION['user_id'] && $_task=='mail') + { + $conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password'])); + if (!$conn) + { + show_message('imaperror', 'error'); + $_SESSION['user_id'] = ''; + } + } + + +// not logged in -> set task to 'login +if (!$_SESSION['user_id']) + $_task = 'login'; + + + +// set taask and action to client +$script = sprintf("%s.set_env('task', '%s');", $JS_OBJECT_NAME, $_task); +if (!empty($_action)) + $script .= sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action); + +$OUTPUT->add_script($script); + + + +// not logged in -> show login page +if (!$_SESSION['user_id']) + { + parse_template('login'); + exit; + } + + + +// include task specific files +if ($_task=='mail') + { + include_once('program/steps/mail/func.inc'); + + if ($_action=='show' || $_action=='print') + include('program/steps/mail/show.inc'); + + if ($_action=='get') + include('program/steps/mail/get.inc'); + + if ($_action=='moveto' || $_action=='delete') + include('program/steps/mail/move_del.inc'); + + if ($_action=='mark') + include('program/steps/mail/mark.inc'); + + if ($_action=='viewsource') + include('program/steps/mail/viewsource.inc'); + + if ($_action=='send') + include('program/steps/mail/sendmail.inc'); + + if ($_action=='upload') + include('program/steps/mail/upload.inc'); + + if ($_action=='compose') + include('program/steps/mail/compose.inc'); + + if ($_action=='addcontact') + include('program/steps/mail/addcontact.inc'); + + if ($_action=='list' && $_GET['_remote']) + include('program/steps/mail/list.inc'); + + // kill compose entry from session + if (isset($_SESSION['compose'])) + rcmail_compose_cleanup(); + } + + +// include task specific files +if ($_task=='addressbook') + { + include_once('program/steps/addressbook/func.inc'); + + if ($_action=='save') + include('program/steps/addressbook/save.inc'); + + if ($_action=='edit' || $_action=='add') + include('program/steps/addressbook/edit.inc'); + + if ($_action=='delete') + include('program/steps/addressbook/delete.inc'); + + if ($_action=='show') + include('program/steps/addressbook/show.inc'); + + if ($_action=='list' && $_GET['_remote']) + include('program/steps/addressbook/list.inc'); + } + + +// include task specific files +if ($_task=='settings') + { + include_once('program/steps/settings/func.inc'); + + if ($_action=='save-identity') + include('program/steps/settings/save_identity.inc'); + + if ($_action=='add-identity' || $_action=='edit-identity') + include('program/steps/settings/edit_identity.inc'); + + if ($_action=='delete-identity') + include('program/steps/settings/delete_identity.inc'); + + if ($_action=='identities') + include('program/steps/settings/identities.inc'); + + if ($_action=='save-prefs') + include('program/steps/settings/save_prefs.inc'); + + if ($_action=='folders' || $_action=='subscribe' || $_action=='unsubscribe' || $_action=='create-folder' || $_action=='delete-folder') + include('program/steps/settings/manage_folders.inc'); + + } + + +// parse main template +parse_template($_task); + +?>
\ No newline at end of file diff --git a/logs/.htaccess b/logs/.htaccess new file mode 100644 index 000000000..8e6a345dc --- /dev/null +++ b/logs/.htaccess @@ -0,0 +1,2 @@ +Order allow,deny +Deny from all
\ No newline at end of file diff --git a/logs/errors b/logs/errors new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/logs/errors diff --git a/logs/sendmail b/logs/sendmail new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/logs/sendmail diff --git a/program/blank.gif b/program/blank.gif Binary files differnew file mode 100644 index 000000000..ea83374c1 --- /dev/null +++ b/program/blank.gif diff --git a/program/include/bugs.inc b/program/include/bugs.inc new file mode 100644 index 000000000..819887cc3 --- /dev/null +++ b/program/include/bugs.inc @@ -0,0 +1,104 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/bugs.inc | + | | + | This file is part of the BQube Webmail client | + | Copyright (C) 2005, BQube Dev - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide error handling and logging functions | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +// throw system error and show error page +function raise_error($arg=array(), $log=FALSE, $terminate=FALSE) + { + global $__page_content, $CONFIG, $OUTPUT, $ERROR_CODE, $ERROR_MESSAGE; + + /* $arg keys: + int code + string type (php, xpath, db, imap, javascript) + string message + sring file + int line + */ + + // report bug (if not incompatible browser) + if ($log && $arg['type'] && $arg['message']) + log_bug($arg); + + // display error page and terminate script + if ($terminate) + { + $ERROR_CODE = $arg['code']; + $ERROR_MESSAGE = $arg['message']; + include("program/steps/error.inc"); + exit; + } + } + + +// report error +function log_bug($arg_arr) + { + global $CONFIG, $INSTALL_PATH; + $program = $arg_arr['type']=='xpath' ? 'XPath' : strtoupper($arg_arr['type']); + + // write error to local log file + if ($CONFIG['debug_level'] & 1) + { + $log_entry = sprintf("[%s] %s Error: %s in %s on line %d\n", + date("d-M-Y H:i:s O", mktime()), + $program, + $arg_arr['message'], + $arg_arr['file'], + $arg_arr['line']); + + if ($fp = fopen($INSTALL_PATH.'logs/errors', 'a')) + { + fwrite($fp, $log_entry); + fclose($fp); + } + } + +/* + // resport the bug to the global bug reporting system + if ($CONFIG['debug_level'] & 2) + { + $delm = '%AC'; + http_request(sprintf('http://roundcube.net/log/bug.php?_type=%s&_domain=%s&_server_ip=%s&_client_ip=%s&_useragent=%s&_url=%s%%3A//%s&_errors=%s%s%s%s%s', + $arg_arr['type'], + $GLOBALS['HTTP_HOST'], + $GLOBALS['SERVER_ADDR'], + $GLOBALS['REMOTE_ADDR'], + rawurlencode($GLOBALS['HTTP_USER_AGENT']), + $GLOBALS['SERVER_PORT']==43 ? 'https' : 'http', + $GLOBALS['HTTP_HOST'].$GLOBALS['REQUEST_URI'], + $arg_arr['file'], $delm, + $arg_arr['line'], $delm, + rawurlencode($arg_arr['message']))); + } +*/ + + // show error if debug_mode is on + if ($CONFIG['debug_level'] & 4) + { + print "<b>$program Error in $arg_arr[file] ($arg_arr[line]):</b> "; + print nl2br($arg_arr['message']); + print '<br />'; + flush(); + } + } + + +?>
\ No newline at end of file diff --git a/program/include/cache.inc b/program/include/cache.inc new file mode 100644 index 000000000..84ed8f07f --- /dev/null +++ b/program/include/cache.inc @@ -0,0 +1,112 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/cache.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide access to the application cache | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +function rcube_read_cache($key) + { + global $DB, $CACHE_KEYS; + + // query db + $sql_result = $DB->query(sprintf("SELECT cache_id, data + FROM %s + WHERE user_id=%d + AND cache_key='%s'", + get_table_name('cache'), + $_SESSION['user_id'], + $key)); + + // get cached data + if ($sql_arr = $DB->fetch_assoc($sql_result)) + { + $data = $sql_arr['data']; + $CACHE_KEYS[$key] = $sql_arr['cache_id']; + } + else + $data = FALSE; + + return $data; + } + + +function rcube_write_cache($key, $data, $session_cache=FALSE) + { + global $DB, $CACHE_KEYS, $sess_id; + + // check if we already have a cache entry for this key + if (!isset($CACHE_KEYS[$key])) + { + $sql_result = $DB->query(sprintf("SELECT cache_id + FROM %s + WHERE user_id=%d + AND cache_key='%s'", + get_table_name('cache'), + $_SESSION['user_id'], + $key)); + + if ($sql_arr = $DB->fetch_assoc($sql_result)) + $CACHE_KEYS[$key] = $sql_arr['cache_id']; + else + $CACHE_KEYS[$key] = FALSE; + } + + // update existing cache record + if ($CACHE_KEYS[$key]) + { + $DB->query(sprintf("UPDATE %s + SET created=NOW(), + data='%s' + WHERE user_id=%d + AND cache_key='%s'", + get_table_name('cache'), + addslashes($data), + $_SESSION['user_id'], + $key)); + } + // add new cache record + else + { + $DB->query(sprintf("INSERT INTO %s + (created, user_id, session_id, cache_key, data) + VALUES (NOW(), %d, %s, '%s', '%s')", + get_table_name('cache'), + $_SESSION['user_id'], + $session_cache ? "'$sess_id'" : 'NULL', + $key, + addslashes($data))); + } + } + + + +function rcube_clear_cache($key) + { + global $DB; + + $DB->query(sprintf("DELETE FROM %s + WHERE user_id=%d + AND cache_key='%s'", + get_table_name('cache'), + $_SESSION['user_id'], + $key)); + } + + +?>
\ No newline at end of file diff --git a/program/include/main.inc b/program/include/main.inc new file mode 100644 index 000000000..8cbc271b1 --- /dev/null +++ b/program/include/main.inc @@ -0,0 +1,1020 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/main.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide basic functions for the webmail package | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +require_once('lib/des.inc'); + + +// register session and connect to server +function rcmail_startup($task='mail') + { + global $sess_id, $sess_auth, $sess_user_lang; + global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB, $JS_OBJECT_NAME; + + // check client + $BROWSER = rcube_browser(); + + // load config file + include_once('config/main.inc.php'); + $CONFIG = is_array($rcmail_config) ? $rcmail_config : array(); + $CONFIG['skin_path'] = $CONFIG['skin_path'] ? preg_replace('/\/$/', '', $CONFIG['skin_path']) : 'skins/default'; + + // load db conf + include_once('config/db.inc.php'); + $CONFIG = array_merge($CONFIG, $rcmail_config); + + + // set PHP error logging according to config + if ($CONFIG['debug_level'] & 1) + { + ini_set('log_errors', 1); + ini_set('error_log', $INSTALL_PATH.'logs/errors'); + } + if ($CONFIG['debug_level'] & 4) + ini_set('display_errors', 1); + else + ini_set('display_errors', 0); + + + // prepare DB connection + if (strtolower($CONFIG['db_type'])=='mysql') + $DB = new rcube_mysql($CONFIG['db_name'], $CONFIG['db_user'], $CONFIG['db_pass'], $CONFIG['db_host']); + + // database not supported + else + { + raise_error(array('code' => 500, + 'type' => 'php', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Database not supported"), TRUE, TRUE); + return; + } + + + // we can use the database for storing session data + if (is_object($DB) && $DB->connect()) + include_once('include/session.inc'); + + + // init session + session_start(); + $sess_id = session_id(); + + // create session and set session vars + if (!$_SESSION['client_id']) + { + $_SESSION['client_id'] = $sess_id; + $_SESSION['user_lang'] = 'en'; + $_SESSION['auth_time'] = mktime(); + $_SESSION['auth'] = rcmail_auth_hash($sess_id, $_SESSION['auth_time']); + unset($GLOBALS['_auth']); + } + + // set session vars global + $sess_auth = $_SESSION['auth']; + $sess_user_lang = $_SESSION['user_lang']; + + + // overwrite config with user preferences + if (is_array($_SESSION['user_prefs'])) + $CONFIG = array_merge($CONFIG, $_SESSION['user_prefs']); + + + // reset some session parameters when changing task + if ($_SESSION['task'] != $task) + unset($_SESSION['page']); + + // set current task to session + $_SESSION['task'] = $task; + + + // create IMAP object + if ($task=='mail') + rcmail_imap_init(); + + + // set localization + if ($CONFIG['locale_string']) + setlocale(LC_ALL, $CONFIG['locale_string']); + else if ($sess_user_lang) + setlocale(LC_ALL, $sess_user_lang); + + + register_shutdown_function('rcmail_shutdown'); + } + + +// create authorization hash +function rcmail_auth_hash($sess_id, $ts) + { + global $CONFIG; + + $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s', + $sess_id, + $ts, + $CONFIG['ip_check'] ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***', + $_SERVER['HTTP_USER_AGENT']); + + if (function_exists('sha1')) + return sha1($auth_string); + else + return md5($auth_string); + } + + + +// create IMAP object and connect to server +function rcmail_imap_init($connect=FALSE) + { + global $CONFIG, $IMAP; + + $IMAP = new rcube_imap(); + + // set root dir from config + if (strlen($CONFIG['imap_root'])) + $IMAP->set_rootdir($CONFIG['imap_root']); + + if (is_array($CONFIG['default_imap_folders'])) + $IMAP->set_default_mailboxes($CONFIG['default_imap_folders']); + + if (strlen($_SESSION['mbox'])) + $IMAP->set_mailbox($_SESSION['mbox']); + + if (isset($_SESSION['page'])) + $IMAP->set_page($_SESSION['page']); + + // set pagesize from config + if (isset($CONFIG['pagesize'])) + $IMAP->set_pagesize($CONFIG['pagesize']); + + + // connect with stored session data + if ($connect) + { + if (!($conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password'])))) + show_message('imaperror', 'error'); + } + } + + +// do these things on script shutdown +function rcmail_shutdown() + { + global $IMAP; + + if (is_object($IMAP)) + { + $IMAP->close(); + $IMAP->write_cache(); + } + } + + +// destroy session data and remove cookie +function rcmail_kill_session() + { +/* $sess_name = session_name(); + if (isset($_COOKIE[$sess_name])) + setcookie($sess_name, '', time()-42000, '/'); +*/ + $_SESSION = array(); + session_destroy(); + } + + +// return correct name for a specific database table +function get_table_name($table) + { + global $CONFIG; + + // return table name if configured + $config_key = 'db_table_'.$table; + + if (strlen($CONFIG[$config_key])) + return $CONFIG[$config_key]; + + return $table; + } + + + +// init output object for GUI and add common scripts +function load_gui() + { + global $CONFIG, $OUTPUT, $COMM_PATH, $IMAP, $JS_OBJECT_NAME; + + // init output page + $OUTPUT = new rcube_html_page(); + + // add common javascripts + $javascript = "var $JS_OBJECT_NAME = new rcube_webmail();\n"; + $javascript .= "$JS_OBJECT_NAME.set_env('comm_path', '$COMM_PATH');\n"; + + if ($_GET['_framed'] || $_POST['_framed']) + $javascript .= "$JS_OBJECT_NAME.set_env('framed', true);\n"; + + $OUTPUT->add_script($javascript); + $OUTPUT->include_script('program/js/common.js'); + $OUTPUT->include_script('program/js/app.js'); + } + + +// perfom login to the IMAP server and to the webmail service +function rcmail_login($user, $pass, $host=NULL) + { + global $CONFIG, $IMAP, $DB, $sess_user_lang; + + if (!$host) + $host = $CONFIG['default_host']; + + // exit if IMAP login failed + if (!($imap_login = $IMAP->connect($host, $user, $pass))) + return FALSE; + + // query if user already registered + $sql_result = $DB->query(sprintf("SELECT user_id, language, preferences + FROM %s + WHERE username='%s' AND mail_host='%s'", + get_table_name('users'), + $user, $host)); + + // user already registered + if ($sql_arr = $DB->fetch_assoc($sql_result)) + { + $user_id = $sql_arr['user_id']; + + // get user prefs + if (strlen($sql_arr['preferences'])) + { + $user_prefs = unserialize($sql_arr['preferences']); + $_SESSION['user_prefs'] = $user_prefs; + array_merge($CONFIG, $user_prefs); + } + + // set user specific language + if (strlen($sql_arr['language'])) + $sess_user_lang = $_SESSION['user_lang'] = $sql_arr['language']; + + // update user's record + $DB->query(sprintf("UPDATE %s + SET last_login=NOW() + WHERE user_id=%d", + get_table_name('users'), + $user_id)); + } + // create new system user + else if ($CONFIG['auto_create_user']) + { + $user_id = rcmail_create_user($user, $host); + } + + if ($user_id) + { + $_SESSION['user_id'] = $user_id; + $_SESSION['imap_host'] = $host; + $_SESSION['username'] = $user; + $_SESSION['password'] = encrypt_passwd($pass); + + // force reloading complete list of subscribed mailboxes + $IMAP->clear_cache('mailboxes'); + + return TRUE; + } + + return FALSE; + } + + +// create new entry in users and identities table +function rcmail_create_user($user, $host) + { + global $DB, $CONFIG, $IMAP; + + $DB->query(sprintf("INSERT INTO %s + (created, last_login, username, mail_host) + VALUES (NOW(), NOW(), '%s', '%s')", + get_table_name('users'), + $user, $host)); + + if ($user_id = $DB->insert_id()) + { + // also create a new identity record + $DB->query(sprintf("INSERT INTO %s + (user_id, `default`, name, email) + VALUES (%d, '1', '%s', '%s@%s')", + get_table_name('identities'), + $user_id, + $user, + $user, + $host)); + + // get existing mailboxes + $a_mailboxes = $IMAP->list_mailboxes(); + + // check if the configured mailbox for sent messages exists + if ($CONFIG['sent_mbox'] && !in_array_nocase($CONFIG['sent_mbox'], $a_mailboxes)) + $IMAP->create_mailbox($CONFIG['sent_mbox'], TRUE); + + // check if the configured mailbox for sent messages exists + if ($CONFIG['trash_mbox'] && !in_array_nocase($CONFIG['trash_mbox'], $a_mailboxes)) + $IMAP->create_mailbox($CONFIG['trash_mbox'], TRUE); + } + + return $user_id; + } + + +function show_message($message, $type='notice') + { + global $OUTPUT, $JS_OBJECT_NAME, $REMOTE_REQUEST; + + $framed = ($_GET['framed'] || $_POST['_framed']); + $command = sprintf("display_message('%s', '%s');", + addslashes(rep_specialchars_output(rcube_label($message))), + $type); + + if ($REMOTE_REQUEST) + return 'this.'.$command; + + else + $OUTPUT->add_script(sprintf("%s%s.%s", + $framed ? sprintf('if(parent.%s)parent.', $JS_OBJECT_NAME) : '', + $JS_OBJECT_NAME, + $command)); + + // console(rcube_label($message)); + } + + +function console($msg, $type=1) + { + print $msg; + print "\n<hr>\n"; + } + + +function encrypt_passwd($pass) + { + $cypher = des('rcmail?24BitPwDkeyF**ECB', $pass, 1, 0, NULL); + return base64_encode($cypher); + } + + +function decrypt_passwd($cypher) + { + $pass = des('rcmail?24BitPwDkeyF**ECB', base64_decode($cypher), 0, 0, NULL); + return trim($pass); + } + + +// send correct response on a remote request +function rcube_remote_response($js_code) + { + send_nocacheing_headers(); + //header('Content-Type: text/javascript'); + header('Content-Type: application/x-javascript'); + + print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n"; + print $js_code; + exit; + } + + + + +// ************** template parsing and gui functions ************** + + +// return boolean if a specific template exists +function template_exists($name) + { + global $CONFIG, $OUTPUT; + $skin_path = $CONFIG['skin_path']; + + // check template file + return is_file("$skin_path/templates/$name.html"); + } + + +// get page template an replace variable +// similar function as used in nexImage +function parse_template($name='main', $exit=TRUE) + { + global $CONFIG, $OUTPUT; + $skin_path = $CONFIG['skin_path']; + + // read template file + $templ = ''; + $path = "$skin_path/templates/$name.html"; + + if($fp = @fopen($path, 'r')) + { + $templ = fread($fp, filesize($path)); + fclose($fp); + } + else + { + raise_error(array('code' => 500, + 'type' => 'php', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Error loading template for '$name'"), TRUE, TRUE); + return FALSE; + } + + + // parse for specialtags + $output = parse_rcube_xml($templ); + + $OUTPUT->write(trim(parse_with_globals($output)), $skin_path); + + if ($exit) + exit; + } + + + +// replace all strings ($varname) with the content of the according global variable +function parse_with_globals($input) + { + $output = preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input); + return $output; + } + + + +function parse_rcube_xml($input) + { + $output = preg_replace('/<roundcube:([-_a-z]+)\s+([^>]+)>/Uie', "rcube_xml_command('\\1', '\\2')", $input); + return $output; + } + + +function rcube_xml_command($command, $str_attrib, $a_attrib=NULL) + { + global $IMAP, $CONFIG; + + $attrib = array(); + $command = strtolower($command); + + preg_match_all('/\s*([-_a-z]+)=["]([^"]+)["]?/i', stripslashes($str_attrib), $regs, PREG_SET_ORDER); + + // convert attributes to an associative array (name => value) + if ($regs) + foreach ($regs as $attr) + $attrib[strtolower($attr[1])] = $attr[2]; + else if ($a_attrib) + $attrib = $a_attrib; + + // execute command + switch ($command) + { + // return a button + case 'button': + if ($attrib['command']) + return rcube_button($attrib); + break; + + // show a label + case 'label': + if ($attrib['name'] || $attrib['command']) + return rcube_label($attrib); + break; + + // create a menu item + case 'menu': + if ($attrib['command'] && $attrib['group']) + rcube_menu($attrib); + break; + + // include a file + case 'include': + $path = realpath($CONFIG['skin_path'].$attrib['file']); + + if($fp = @fopen($path, 'r')) + { + $incl = fread($fp, filesize($path)); + fclose($fp); + return parse_rcube_xml($incl); + } + break; + + // return code for a specific application object + case 'object': + $object = strtolower($attrib['name']); + + if ($object=='loginform') + return rcmail_login_form($attrib); + + else if ($object=='message') + return rcmail_message_container($attrib); + + // MAIL + else if ($object=='mailboxlist' && function_exists('rcmail_mailbox_list')) + return rcmail_mailbox_list($attrib); + + else if ($object=='messages' && function_exists('rcmail_message_list')) + return rcmail_message_list($attrib); + + else if ($object=='messagecountdisplay' && function_exists('rcmail_messagecount_display')) + return rcmail_messagecount_display($attrib); + + else if ($object=='messageheaders' && function_exists('rcmail_message_headers')) + return rcmail_message_headers($attrib); + + else if ($object=='messageattachments' && function_exists('rcmail_message_attachments')) + return rcmail_message_attachments($attrib); + + else if ($object=='messagebody' && function_exists('rcmail_message_body')) + return rcmail_message_body($attrib); + + else if ($object=='blockedobjects' && function_exists('rcmail_remote_objects_msg')) + return rcmail_remote_objects_msg($attrib); + + else if ($object=='messagecontentframe' && function_exists('rcmail_messagecontent_frame')) + return rcmail_messagecontent_frame($attrib); + + else if ($object=='messagepartframe' && function_exists('rcmail_message_part_frame')) + return rcmail_message_part_frame($attrib); + + else if ($object=='messagepartcontrols' && function_exists('rcmail_message_part_controls')) + return rcmail_message_part_controls($attrib); + + else if ($object=='composeheaders' && function_exists('rcmail_compose_headers')) + return rcmail_compose_headers($attrib); + + else if ($object=='composesubject' && function_exists('rcmail_compose_subject')) + return rcmail_compose_subject($attrib); + + else if ($object=='composebody' && function_exists('rcmail_compose_body')) + return rcmail_compose_body($attrib); + + else if ($object=='composeattachmentlist' && function_exists('rcmail_compose_attachment_list')) + return rcmail_compose_attachment_list($attrib); + + else if ($object=='composeattachmentform' && function_exists('rcmail_compose_attachment_form')) + return rcmail_compose_attachment_form($attrib); + + else if ($object=='composeattachment' && function_exists('rcmail_compose_attachment_field')) + return rcmail_compose_attachment_field($attrib); + + else if ($object=='priorityselector' && function_exists('rcmail_priority_selector')) + return rcmail_priority_selector($attrib); + + else if ($object=='priorityselector' && function_exists('rcmail_priority_selector')) + return rcmail_priority_selector($attrib); + + + // ADDRESS BOOK + else if ($object=='addresslist' && function_exists('rcmail_contacts_list')) + return rcmail_contacts_list($attrib); + + else if ($object=='addressframe' && function_exists('rcmail_contact_frame')) + return rcmail_contact_frame($attrib); + + else if ($object=='recordscountdisplay' && function_exists('rcmail_rowcount_display')) + return rcmail_rowcount_display($attrib); + + else if ($object=='contactdetails' && function_exists('rcmail_contact_details')) + return rcmail_contact_details($attrib); + + else if ($object=='contacteditform' && function_exists('rcmail_contact_editform')) + return rcmail_contact_editform($attrib); + + + // USER SETTINGS + else if ($object=='userprefs' && function_exists('rcmail_user_prefs_form')) + return rcmail_user_prefs_form($attrib); + + else if ($object=='itentitieslist' && function_exists('rcmail_identities_list')) + return rcmail_identities_list($attrib); + + else if ($object=='identityframe' && function_exists('rcmail_identity_frame')) + return rcmail_identity_frame($attrib); + + else if ($object=='identityform' && function_exists('rcube_identity_form')) + return rcube_identity_form($attrib); + + else if ($object=='foldersubscription' && function_exists('rcube_subscription_form')) + return rcube_subscription_form($attrib); + + else if ($object=='createfolder' && function_exists('rcube_create_folder_form')) + return rcube_create_folder_form($attrib); + + + else if ($object=='pagetitle') + { + $task = $GLOBALS['_task']; + if ($task=='mail' && isset($GLOBALS['MESSAGE']['subject'])) + return rep_specialchars_output("RoundCube|Mail :: ".$GLOBALS['MESSAGE']['subject']); + else if (isset($GLOBALS['PAGE_TITLE'])) + return rep_specialchars_output("RoundCube|Mail :: ".$GLOBALS['PAGE_TITLE']); + else if ($task=='mail' && ($mbox_name = $IMAP->get_mailbox_name())) + return "RoundCube|Mail :: $mbox_name"; + else + return "RoundCube|Mail :: $task"; + } + + else if ($object=='about') + return ''; + + break; + } + + return ''; + } + + +// create and register a button +function rcube_button($attrib) + { + global $CONFIG, $OUTPUT, $JS_OBJECT_NAME; + static $sa_buttons = array(); + static $s_button_count = 100; + + $skin_path = $CONFIG['skin_path']; + + if (!($attrib['command'] || $attrib['name'])) + return ''; + + // try to find out the button type + if ($attrib['type']) + $attrib['type'] = strtolower($attrib['type']); + else + $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $arg['imagect']) ? 'image' : 'link'; + + + $command = $attrib['command']; + + // take the button from the stack + if($attrib['name'] && $sa_buttons[$attrib['name']]) + $attrib = $sa_buttons[$attrib['name']]; + + // add button to button stack + else if($attrib['image'] || $arg['imagect'] || $attrib['imagepas'] || $attrib['class']) + { + if(!$attrib['name']) + $attrib['name'] = $command; + + if (!$attrib['image']) + $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact']; + + $sa_buttons[$attrib['name']] = $attrib; + } + + // get saved button for this command/name + else if ($command && $sa_buttons[$command]) + $attrib = $sa_buttons[$command]; + + //else + // return ''; + + + // set border to 0 because of the link arround the button + if ($attrib['type']=='image' && !isset($attrib['border'])) + $attrib['border'] = 0; + + if (!$attrib['id']) + $attrib['id'] = sprintf('rcmbtn%d', $s_button_count++); + + // get localized text for labels and titles + if ($attrib['title']) + $attrib['title'] = rep_specialchars_output(rcube_label($attrib['title'])); + if ($attrib['label']) + $attrib['label'] = rep_specialchars_output(rcube_label($attrib['label'])); + + if ($attrib['alt']) + $attrib['alt'] = rep_specialchars_output(rcube_label($attrib['alt'])); + + // add empty alt attribute for XHTML compatibility + if (!isset($attrib['alt'])) + $attrib['alt'] = ''; + + + // register button in the system + if ($attrib['command']) + $OUTPUT->add_script(sprintf("%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');", + $JS_OBJECT_NAME, + $command, + $attrib['id'], + $attrib['type'], + $attrib['imageact'] ? $skin_path.$attrib['imageact'] : $attrib['classact'], + $attirb['imagesel'] ? $skin_path.$attirb['imagesel'] : $attrib['classsel'], + $attrib['imageover'] ? $skin_path.$attrib['imageover'] : '')); + + // overwrite attributes + if (!$attrib['href']) + $attrib['href'] = '#'; + + if ($command) + $attrib['onclick'] = sprintf("return %s.command('%s','%s',this)", $JS_OBJECT_NAME, $command, $attrib['prop']); + + if ($command && $attrib['imageover']) + { + $attrib['onmouseover'] = sprintf("return %s.button_over('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); + $attrib['onmouseout'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); + } + + + $out = ''; + + // generate image tag + if ($attrib['type']=='image') + { + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'alt')); + $img_tag = sprintf('<img src="%%s"%s />', $attrib_str); + $btn_content = sprintf($img_tag, $skin_path.$attrib['image']); + if ($attrib['label']) + $btn_content .= ' '.$attrib['label']; + + $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'title'); + } + else if ($attrib['type']=='link') + { + $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command']; + $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style'); + } + else if ($attrib['type']=='input') + { + $attrib['type'] = 'button'; + + if ($attrib['label']) + $attrib['value'] = $attrib['label']; + + $attrib_str = create_attrib_string($attrib, array('type', 'value', 'onclick', 'id', 'class', 'style')); + $out = sprintf('<input%s disabled />', $attrib_str); + } + + // generate html code for button + if ($btn_content) + { + $attrib_str = create_attrib_string($attrib, $link_attrib); + $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content); + } + + return $out; + } + + +function rcube_menu($attrib) + { + + return ''; + } + + + +function rcube_table_output($attrib, $sql_result, $a_show_cols, $id_col) + { + global $DB; + + // 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')); + + $table = '<table' . $attrib_str . ">\n"; + + // add table title + $table .= "<thead><tr>\n"; + + foreach ($a_show_cols as $col) + $table .= '<td class="'.$col.'">' . rcube_label($col) . "</td>\n"; + + $table .= "</tr></thead>\n<tbody>\n"; + + $c = 0; + while ($sql_result && ($sql_arr = $DB->fetch_assoc($sql_result))) + { + $zebra_class = $c%2 ? 'even' : 'odd'; + + $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]); + + // format each col + foreach ($a_show_cols as $col) + { + $cont = rep_specialchars_output($sql_arr[$col]); + $table .= '<td class="'.$col.'">' . $cont . "</td>\n"; + } + + $table .= "</tr>\n"; + $c++; + } + + // complete message table + $table .= "</tbody></table>\n"; + + return $table; + } + + + +function rcmail_get_edit_field($col, $value, $attrib, $type='text') + { + $fname = '_'.$col; + $attrib['name'] = $fname; + + if ($type=='checkbox') + { + $attrib['value'] = '1'; + $input = new checkbox($attrib); + } + else if ($type=='textarea') + { + $attrib['cols'] = $attrib['size']; + $input = new textarea($attrib); + } + else + $input = new textfield($attrib); + + // use value from post + if ($_POST[$fname]) + $value = $_POST[$fname]; + + $out = $input->show($value); + + return $out; + } + + +function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style')) + { + // allow the following attributes to be added to the <iframe> tag + $attrib_str = ''; + foreach ($allowed_attribs as $a) + if (isset($attrib[$a])) + $attrib_str .= sprintf(' %s="%s"', $a, $attrib[$a]); + + return $attrib_str; + } + + + +function format_date($date, $format=NULL) + { + global $CONFIG, $sess_user_lang; + + if (is_numeric($date)) + $ts = $date; + else + $ts = strtotime($date); + + // convert time to user's timezone + $timestamp = $ts - date('Z', $ts) + ($CONFIG['timezone'] * 3600); + + // get current timestamp in user's timezone + $now = time(); // local time + $now -= (int)date('Z'); // make GMT time + $now += ($CONFIG['timezone'] * 3600); // user's time + + $day_secs = 60*((int)date('H', $now)*60 + (int)date('i', $now)); + $week_secs = 60 * 60 * 24 * 7; + $diff = $now - $timestamp; + + // define daate format depending on current time + if (!$format && $diff < $day_secs) + return sprintf('%s %s', rcube_label('today'), date('H:i', $timestamp)); + else if (!$format && $diff < $week_secs) + $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i'; + else if (!$format) + $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i'; + + + // parse format string manually in order to provide localized weekday and month names + // an alternative would be to convert the date() format string to fit with strftime() + $out = ''; + for($i=0; $i<strlen($format); $i++) + { + if ($format{$i}=='\\') // skip escape chars + continue; + + // write char "as-is" + if ($format{$i}==' ' || $format{$i-1}=='\\') + $out .= $format{$i}; + // weekday (short) + else if ($format{$i}=='D') + $out .= rcube_label(strtolower(date('D', $timestamp))); + // weekday long + else if ($format{$i}=='l') + $out .= rcube_label(strtolower(date('l', $timestamp))); + // month name (short) + else if ($format{$i}=='M') + $out .= rcube_label(strtolower(date('M', $timestamp))); + // month name (long) + else if ($format{$i}=='F') + $out .= rcube_label(strtolower(date('F', $timestamp))); + else + $out .= date($format{$i}, $timestamp); + } + + return $out; + } + + +// ************** functions delivering gui objects ************** + + + +function rcmail_message_container($attrib) + { + global $OUTPUT, $JS_OBJECT_NAME; + + if (!$attrib['id']) + $attrib['id'] = 'rcmMessageContainer'; + + // allow the following attributes to be added to the <table> tag + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); + $out = '<div' . $attrib_str . "></div>"; + + $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('message', '$attrib[id]');"); + + return $out; + } + + +// return code for the webmail login form +function rcmail_login_form($attrib) + { + global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD; + + $labels = array(); + $labels['user'] = rcube_label('username'); + $labels['pass'] = rcube_label('password'); + $labels['host'] = rcube_label('server'); + + $input_user = new textfield(array('name' => '_user', 'size' => 30)); + $input_pass = new passwordfield(array('name' => '_pass', 'size' => 30)); + $input_action = new hiddenfield(array('name' => '_action', 'value' => 'login')); + + $fields = array(); + $fields['user'] = $input_user->show($_POST['_user']); + $fields['pass'] = $input_pass->show(); + $fields['action'] = $input_action->show(); + + if (is_array($CONFIG['default_host'])) + { + $select_host = new select(array('name' => '_host')); + $select_host->add($CONFIG['default_host']); + $fields['host'] = $select_host->show($_POST['_host']); + } + else if (!strlen($CONFIG['default_host'])) + { + $input_host = new textfield(array('name' => '_host', 'size' => 30)); + $fields['host'] = $input_host->show($_POST['_host']); + } + + $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form'; + $form_start = !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : ''; + $form_end = !strlen($attrib['form']) ? '</form>' : ''; + + if ($fields['host']) + $form_host = <<<EOF + +</tr><tr> + +<td class="title">$labels[host]</td> +<td>$fields[host]</td> + +EOF; + + $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('loginform', '$form_name');"); + + $out = <<<EOF +$form_start +$SESS_HIDDEN_FIELD +$fields[action] +<table><tr> + +<td class="title">$labels[user]</td> +<td>$fields[user]</td> + +</tr><tr> + +<td class="title">$labels[pass]</td> +<td>$fields[pass]</td> +$form_host +</tr></table> +$form_end +EOF; + + return $out; + } + + +?>
\ No newline at end of file diff --git a/program/include/rcube_imap.inc b/program/include/rcube_imap.inc new file mode 100644 index 000000000..594dbf40f --- /dev/null +++ b/program/include/rcube_imap.inc @@ -0,0 +1,1117 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/rcube_imap.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | IMAP wrapper that implements the Iloha IMAP Library (IIL) | + | See http://ilohamail.org/ for details | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +require_once('lib/imap.inc'); +require_once('lib/mime.inc'); + +// check for Open-SSL support in PHP build +//$ICL_SSL = TRUE; +//$ICL_PORT = 993; + +class rcube_imap + { + var $conn; + var $root_dir = ''; + var $mailbox = 'INBOX'; + var $list_page = 1; + var $page_size = 10; + var $cacheing_enabled = FALSE; + var $default_folders = array('inbox', 'drafts', 'sent', 'junk', 'trash'); + var $cache = array(); + var $cache_changes = array(); + var $uid_id_map = array(); + var $msg_headers = array(); + + + // PHP 5 constructor + function __construct() + { + if (function_exists('rcube_read_cache')) + $this->cacheing_enabled = TRUE; + } + + // PHP 4 compatibility + function rcube_imap() + { + $this->__construct(); + } + + + function iloha_imap($connection='') + { + if ($connection) + { + $a_url = parse_url($connection); + $scheme = $a_url['scheme'] ? $a_url['scheme'] : 'imap'; + $port = $a_url['port'] ? $a_url['port'] : ($scheme=='imaps' ? 993 : 143); + $host = $a_url['host']; + $user = $a_url['user']; + $pass = $a_url['pass']; + + //var_dump($a_url); + + $this->connect($host, $user, $pass, $port); + } + } + + + function connect($host, $user, $pass, $port=143) + { + global $ICL_PORT; + + $ICL_PORT = $port; + $this->conn = iil_Connect($host, $user, $pass); + $this->host = $host; + $this->user = $user; + $this->pass = $pass; + + return $this->conn ? TRUE : FALSE; + } + + + function close() + { + if ($this->conn) + iil_Close($this->conn); + } + + + function set_rootdir($root) + { + if (substr($root, -1, 1)==='/') + $root = substr($root, 0, -1); + + $this->root_dir = $root; + } + + + function set_default_mailboxes($arr) + { + if (is_array($arr)) + { + $this->default_folders = array(); + + // add mailbox names lower case + foreach ($arr as $mbox) + $this->default_folders[] = strtolower($mbox); + + // add inbox if not included + if (!in_array('inbox', $this->default_folders)) + array_unshift($arr, 'inbox'); + } + } + + + function set_mailbox($mbox) + { + $mailbox = $this->_mod_mailbox($mbox); + + if ($this->mailbox == $mailbox) + return; + + $this->mailbox = $mailbox; + + // clear messagecount cache for this mailbox + $this->_clear_messagecount($mailbox); + } + + + function set_page($page) + { + $this->list_page = (int)$page; + } + + + function set_pagesize($size) + { + $this->page_size = (int)$size; + } + + + function get_mailbox_name() + { + return $this->conn ? $this->_mod_mailbox($this->mailbox, 'out') : ''; + } + + + // public method for mailbox listing + // convert mailbox name with root dir first + function list_mailboxes($root='', $filter='*') + { + $a_out = array(); + $a_mboxes = $this->_list_mailboxes($root, $filter); + + foreach ($a_mboxes as $mbox) + { + $name = $this->_mod_mailbox($mbox, 'out'); + if (strlen($name)) + $a_out[] = $name; + } + + // sort mailboxes + $a_out = $this->_sort_mailbox_list($a_out); + + return $a_out; + } + + // private method for mailbox listing + function _list_mailboxes($root='', $filter='*') + { + $a_defaults = $a_out = array(); + + // get cached folder list + $a_mboxes = $this->get_cache('mailboxes'); + if (is_array($a_mboxes)) + return $a_mboxes; + + // retrieve list of folders from IMAP server + $a_folders = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox($root), $filter); + + if (!is_array($a_folders) || !sizeof($a_folders)) + $a_folders = array(); + + // create INBOX if it does not exist + if (!in_array_nocase('INBOX', $a_folders)) + { + $this->create_mailbox('INBOX', TRUE); + array_unshift($a_folders, 'INBOX'); + } + + $a_mailbox_cache = array(); + + // write mailboxlist to cache + $this->update_cache('mailboxes', $a_folders); + + return $a_folders; + } + + + // get message count for a specific mailbox; acceptes modes are: ALL, UNSEEN + function messagecount($mbox='', $mode='ALL', $force=FALSE) + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + return $this->_messagecount($mailbox, $mode, $force); + } + + // private method for getting nr of mesages + function _messagecount($mailbox='', $mode='ALL', $force=FALSE) + { + $a_mailbox_cache = FALSE; + $mode = strtoupper($mode); + + if (!$mailbox) + $mailbox = $this->mailbox; + + $a_mailbox_cache = $this->get_cache('messagecount'); + + // return cached value + if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode])) + return $a_mailbox_cache[$mailbox][$mode]; + + // get message count and store in cache + if ($mode == 'UNSEEN') + $count = iil_C_CountUnseen($this->conn, $mailbox); + else + $count = iil_C_CountMessages($this->conn, $mailbox); + +//print "/**** get messagecount for $mailbox ($mode): $count ****/\n"; + + if (is_array($a_mailbox_cache[$mailbox])) + $a_mailbox_cache[$mailbox] = array(); + + $a_mailbox_cache[$mailbox][$mode] = (int)$count; + + // write back to cache + $this->update_cache('messagecount', $a_mailbox_cache); + +//var_dump($a_mailbox_cache); + + return (int)$count; + } + + + // public method for listing headers + // convert mailbox name with root dir first + function list_headers($mbox='', $page=NULL, $sort_field='date', $sort_order='DESC') + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + return $this->_list_headers($mailbox, $page, $sort_field, $sort_order); + } + + + // private method for listing message header + function _list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') + { + $max = $this->_messagecount($mailbox /*, 'ALL', TRUE*/); + $a_out = array(); + + if (!strlen($mailbox)) + return $a_out; + + + // get cached headers + $a_msg_headers = $this->get_cache($mailbox.'.msg'); + +// print "/**** count = $max; headers = ".sizeof($a_msg_headers)." ****/\n"; + + // retrieve headers from IMAP + if (!is_array($a_msg_headers) || sizeof($a_msg_headers) != $max) + { + $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, "1:$max"); + $a_msg_headers = array(); + foreach ($a_header_index as $i => $headers) + $a_msg_headers[$headers->uid] = $headers; + +// print "/**** fetch headers ****/\n"; + } + else + $headers_cached = TRUE; + + // sort headers by a specific col + $a_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order); + + // write headers list to cache + if (!$headers_cached) + $this->update_cache($mailbox.'.msg', $a_msg_headers); + + if (is_array($a_headers)) + foreach ($a_headers as $header) + if (!$header->deleted) + $a_out[] = $header; + + // return complete list of messages + if (strtolower($page)=='all') + return $a_out; + + $start_msg = ($this->list_page-1) * $this->page_size; + return array_slice($a_out, $start_msg, $this->page_size); + } + + + // return sorted array of message UIDs + function message_index($mbox='', $sort_field='date', $sort_order='DESC') + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + $a_out = array(); + + // get array of message headers + $a_headers = $this->_list_headers($mailbox, 'all', $sort_field, $sort_order); + + if (is_array($a_headers)) + foreach ($a_headers as $header) + $a_out[] = $header->uid; + + return $a_out; + } + + + function sync_header_index($mbox=NULL) + { + + } + + + function search($mbox='', $criteria='ALL') + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + $a_messages = iil_C_Search($this->conn, $mailbox, $criteria); + return $a_messages; + } + + + function get_headers($uid, $mbox=NULL) + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + + // get cached headers + $a_msg_headers = $this->get_cache($mailbox.'.msg'); + + // return cached header + if ($a_msg_headers[$uid]) + return $a_msg_headers[$uid]; + + $msg_id = $this->_uid2id($uid); + $header = iil_C_FetchHeader($this->conn, $mailbox, $msg_id); + + // write headers cache + $a_msg_headers[$uid] = $header; + $this->update_cache($mailbox.'.msg', $a_msg_headers); + + return $header; + } + + + function get_body($uid, $part=1) + { + if (!($msg_id = $this->_uid2id($uid))) + return FALSE; + + $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); + $structure = iml_GetRawStructureArray($structure_str); + $body = iil_C_FetchPartBody($this->conn, $this->mailbox, $msg_id, $part); + + $encoding = iml_GetPartEncodingCode($structure, $part); + + if ($encoding==3) $body = $this->mime_decode($body, 'base64'); + else if ($encoding==4) $body = $this->mime_decode($body, 'quoted-printable'); + + return $body; + } + + + function get_raw_body($uid) + { + if (!($msg_id = $this->_uid2id($uid))) + return FALSE; + + $body = iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL); + $body .= iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 1); + + return $body; + } + + + // set message flag to one or several messages + // possible flgs are: SEEN, DELETED, RECENT, ANSWERED, DRAFT + function set_flag($uids, $flag) + { + $flag = strtoupper($flag); + $msg_ids = array(); + if (!is_array($uids)) + $uids = array($uids); + + foreach ($uids as $uid) + $msg_ids[] = $this->_uid2id($uid); + + if ($flag=='UNSEEN') + $result = iil_C_Unseen($this->conn, $this->mailbox, join(',', $msg_ids)); + else + $result = iil_C_Flag($this->conn, $this->mailbox, join(',', $msg_ids), $flag); + + + // reload message headers if cached + $cache_key = $this->mailbox.'.msg'; + if ($result && ($a_cached_headers = $this->get_cache($cache_key))) + { + foreach ($uids as $uid) + { + if (isset($a_cached_headers[$uid])) + { + unset($this->cache[$cache_key][$uid]); + $this->get_headers($uid); + } + } + } + + // set nr of messages that were flaged + $count = sizeof($msg_ids); + + // clear message count cache + if ($result && $flag=='SEEN') + $this->_set_messagecount($this->mailbox, 'UNSEEN', $count*(-1)); + else if ($result && $flag=='UNSEEN') + $this->_set_messagecount($this->mailbox, 'UNSEEN', $count); + else if ($result && $flag=='DELETED') + $this->_set_messagecount($this->mailbox, 'ALL', $count*(-1)); + + return $result; + } + + + // append a mail message (source) to a specific mailbox + function save_message($mbox, $message) + { + $mailbox = $this->_mod_mailbox($mbox); + + // make shure mailbox exists + if (in_array($mailbox, $this->_list_mailboxes())) + $saved = iil_C_Append($this->conn, $mailbox, $message); + + if ($saved) + { + // increase messagecount of the target mailbox + $this->_set_messagecount($mailbox, 'ALL', 1); + } + + return $saved; + } + + + // move a message from one mailbox to another + function move_message($uids, $to_mbox, $from_mbox='') + { + $to_mbox = $this->_mod_mailbox($to_mbox); + $from_mbox = $from_mbox ? $this->_mod_mailbox($from_mbox) : $this->mailbox; + + // make shure mailbox exists + if (!in_array($to_mbox, $this->_list_mailboxes())) + return FALSE; + + // convert the list of uids to array + $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL); + + // exit if no message uids are specified + if (!is_array($a_uids)) + return false; + + // convert uids to message ids + $a_mids = array(); + foreach ($a_uids as $uid) + $a_mids[] = $this->_uid2id($uid, $from_mbox); + + $moved = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox); + + // send expunge command in order to have the moved message + // really deleted from the source mailbox + if ($moved) + { + $this->expunge($from_mbox, FALSE); + $this->clear_cache($to_mbox.'.msg'); + $this->_clear_messagecount($from_mbox); + $this->_clear_messagecount($to_mbox); + } + + // update cached message headers + $cache_key = $from_mbox.'.msg'; + if ($moved && ($a_cached_headers = $this->get_cache($cache_key))) + { + foreach ($a_uids as $uid) + unset($a_cached_headers[$uid]); + + $this->update_cache($cache_key, $a_cached_headers); + } + + return $moved; + } + + + // mark messages as deleted and expunge mailbox + function delete_message($uids, $mbox='') + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + + // convert the list of uids to array + $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL); + + // exit if no message uids are specified + if (!is_array($a_uids)) + return false; + + + // convert uids to message ids + $a_mids = array(); + foreach ($a_uids as $uid) + $a_mids[] = $this->_uid2id($uid, $mailbox); + + $deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_mids)); + + // send expunge command in order to have the deleted message + // really deleted from the mailbox + if ($deleted) + { + $this->expunge($mailbox, FALSE); + $this->_clear_messagecount($mailbox); + } + + // remove deleted messages from cache + if ($deleted && ($a_cached_headers = $this->get_cache($mailbox.'.msg'))) + { + foreach ($a_uids as $uid) + unset($a_cached_headers[$uid]); + + $this->update_cache($mailbox.'.msg', $a_cached_headers); + } + + return $deleted; + + } + + + // send IMAP expunge command and clear cache + function expunge($mbox='', $clear_cache=TRUE) + { + $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; + + $result = iil_C_Expunge($this->conn, $mailbox); + + if ($result>=0 && $clear_cache) + { + $this->clear_cache($mailbox.'.msg'); + $this->_clear_messagecount($mailbox); + } + + return $result; + } + + + + /* -------------------------------- + * folder managment + * --------------------------------*/ + + + // return an array with all folders available in IMAP server + function list_unsubscribed($root='') + { + static $sa_unsubscribed; + + if (is_array($sa_unsubscribed)) + return $sa_unsubscribed; + + // retrieve list of folders from IMAP server + $a_mboxes = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox($root), '*'); + + // modify names with root dir + foreach ($a_mboxes as $mbox) + { + $name = $this->_mod_mailbox($mbox, 'out'); + if (strlen($name)) + $a_folders[] = $name; + } + + // filter folders and sort them + $sa_unsubscribed = $this->_sort_mailbox_list($a_folders); + return $sa_unsubscribed; + } + + + // subscribe to a specific mailbox(es) + function subscribe($mbox, $mode='subscribe') + { + if (is_array($mbox)) + $a_mboxes = $mbox; + else if (is_string($mbox) && strlen($mbox)) + $a_mboxes = explode(',', $mbox); + + // let this common function do the main work + return $this->_change_subscription($a_mboxes, 'subscribe'); + } + + + // unsubscribe mailboxes + function unsubscribe($mbox) + { + if (is_array($mbox)) + $a_mboxes = $mbox; + else if (is_string($mbox) && strlen($mbox)) + $a_mboxes = explode(',', $mbox); + + // let this common function do the main work + return $this->_change_subscription($a_mboxes, 'unsubscribe'); + } + + + // create a new mailbox on the server and register it in local cache + function create_mailbox($name, $subscribe=FALSE) + { + $result = FALSE; + $abs_name = $this->_mod_mailbox($name); + $a_mailbox_cache = $this->get_cache('mailboxes'); + + if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array($abs_name, $a_mailbox_cache))) + $result = iil_C_CreateFolder($this->conn, $abs_name); + + // update mailboxlist cache + if ($result && $subscribe) + $this->subscribe($name); + + return $result; + } + + + // set a new name to an existing mailbox + function rename_mailbox($mbox, $new_name) + { + // not implemented yet + } + + + // remove mailboxes from server + function delete_mailbox($mbox) + { + $deleted = FALSE; + + if (is_array($mbox)) + $a_mboxes = $mbox; + else if (is_string($mbox) && strlen($mbox)) + $a_mboxes = explode(',', $mbox); + + if (is_array($a_mboxes)) + foreach ($a_mboxes as $mbox) + { + $mailbox = $this->_mod_mailbox($mbox); + + // unsubscribe mailbox before deleting + iil_C_UnSubscribe($this->conn, $mailbox); + + // send delete command to server + $result = iil_C_DeleteFolder($this->conn, $mailbox); + if ($result>=0) + $deleted = TRUE; + } + + // clear mailboxlist cache + if ($deleted) + $this->clear_cache('mailboxes'); + + return $updated; + } + + + + + /* -------------------------------- + * internal cacheing functions + * --------------------------------*/ + + + function get_cache($key) + { + // read cache + if (!isset($this->cache[$key]) && $this->cacheing_enabled) + { + $cache_data = rcube_read_cache('IMAP.'.$key); + $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE; + } + + return $this->cache[$key]; + } + + + function update_cache($key, $data) + { + $this->cache[$key] = $data; + $this->cache_changed = TRUE; + $this->cache_changes[$key] = TRUE; + } + + + function write_cache() + { + if ($this->cacheing_enabled && $this->cache_changed) + { + foreach ($this->cache as $key => $data) + { + if ($this->cache_changes[$key]) + rcube_write_cache('IMAP.'.$key, serialize($data)); + } + } + } + + + function clear_cache($key=NULL) + { + if ($key===NULL) + { + foreach ($this->cache as $key => $data) + rcube_clear_cache('IMAP.'.$key); + + $this->cache = array(); + $this->cache_changed = FALSE; + $this->cache_changes = array(); + } + else + { + rcube_clear_cache('IMAP.'.$key); + $this->cache_changes[$key] = FALSE; + unset($this->cache[$key]); + } + } + + + + /* -------------------------------- + * encoding/decoding functions + * --------------------------------*/ + + + function decode_address_list($input, $max=NULL) + { + $a = $this->_parse_address_list($input); + $out = array(); + + if (!is_array($a)) + return $out; + + $c = count($a); + $j = 0; + + foreach ($a as $val) + { + $j++; + $address = $val['address']; + $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name'])); + $string = $name!==$address ? sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address) : $address; + + $out[$j] = array('name' => $name, + 'mailto' => $address, + 'string' => $string); + + if ($max && $j==$max) + break; + } + + return $out; + } + + + function decode_header($input) + { + $out = ''; + + $pos = strpos($input, '=?'); + if ($pos !== false) + { + $out = substr($input, 0, $pos); + + $end_cs_pos = strpos($input, "?", $pos+2); + $end_en_pos = strpos($input, "?", $end_cs_pos+1); + $end_pos = strpos($input, "?=", $end_en_pos+1); + + $encstr = substr($input, $pos+2, ($end_pos-$pos-2)); + $rest = substr($input, $end_pos+2); + + $out .= $this->decode_mime_string($encstr); + $out .= $this->decode_header($rest); + + return $out; + } + else + return $input; + } + + + function decode_mime_string($str) + { + $a = explode('?', $str); + $count = count($a); + + // should be in format "charset?encoding?base64_string" + if ($count >= 3) + { + for ($i=2; $i<$count; $i++) + $rest.=$a[$i]; + + if (($a[1]=="B")||($a[1]=="b")) + $rest = base64_decode($rest); + else if (($a[1]=="Q")||($a[1]=="q")) + { + $rest = str_replace("_", " ", $rest); + $rest = quoted_printable_decode($rest); + } + + return decode_specialchars($rest, $a[0]); + } + else + return $str; //we dont' know what to do with this + } + + + function mime_decode($input, $encoding='7bit') + { + switch (strtolower($encoding)) + { + case '7bit': + return $input; + break; + + case 'quoted-printable': + return quoted_printable_decode($input); + break; + + case 'base64': + return base64_decode($input); + break; + + default: + return $input; + } + } + + + function mime_encode($input, $encoding='7bit') + { + switch ($encoding) + { + case 'quoted-printable': + return quoted_printable_encode($input); + break; + + case 'base64': + return base64_encode($input); + break; + + default: + return $input; + } + } + + + // convert body chars according to the ctype_parameters + function charset_decode($body, $ctype_param) + { + if (is_array($ctype_param) && strlen($ctype_param['charset'])) + return decode_specialchars($body, $ctype_param['charset']); + + return $body; + } + + + /* -------------------------------- + * private methods + * --------------------------------*/ + + + function _mod_mailbox($mbox, $mode='in') + { + if ($this->root_dir && $mode=='in') + $mbox = $this->root_dir.'/'.$mbox; + else if ($this->root_dir && $mode=='out') + $mbox = substr($mbox, strlen($this->root_dir)+1); + + return $mbox; + } + + + // sort mailboxes first by default folders and then in alphabethical order + function _sort_mailbox_list($a_folders) + { + $a_out = $a_defaults = array(); + + // find default folders and skip folders starting with '.' + foreach($a_folders as $i => $folder) + { + if ($folder{0}=='.') + continue; + + if (($p = array_search(strtolower($folder), $this->default_folders))!==FALSE) + $a_defaults[$p] = $folder; + else + $a_out[] = $folder; + } + + sort($a_out); + ksort($a_defaults); + + return array_merge($a_defaults, $a_out); + } + + + function _uid2id($uid, $mbox=NULL) + { + if (!$mbox) + $mbox = $this->mailbox; + + if (!isset($this->uid_id_map[$mbox][$uid])) + $this->uid_id_map[$mbox][$uid] = iil_C_UID2ID($this->conn, $mbox, $uid); + + return $this->uid_id_map[$mbox][$uid]; + } + + + // subscribe/unsubscribe a list of mailboxes and update local cache + function _change_subscription($a_mboxes, $mode) + { + $updated = FALSE; + + if (is_array($a_mboxes)) + foreach ($a_mboxes as $i => $mbox) + { + $mailbox = $this->_mod_mailbox($mbox); + $a_mboxes[$i] = $mailbox; + + if ($mode=='subscribe') + $result = iil_C_Subscribe($this->conn, $mailbox); + else if ($mode=='unsubscribe') + $result = iil_C_UnSubscribe($this->conn, $mailbox); + + if ($result>=0) + $updated = TRUE; + } + + // get cached mailbox list + if ($updated) + { + $a_mailbox_cache = $this->get_cache('mailboxes'); + if (!is_array($a_mailbox_cache)) + return $updated; + + // modify cached list + if ($mode=='subscribe') + $a_mailbox_cache = array_merge($a_mailbox_cache, $a_mboxes); + else if ($mode=='unsubscribe') + $a_mailbox_cache = array_diff($a_mailbox_cache, $a_mboxes); + + // write mailboxlist to cache + $this->update_cache('mailboxes', $this->_sort_mailbox_list($a_mailbox_cache)); + } + + return $updated; + } + + + // increde/decrese messagecount for a specific mailbox + function _set_messagecount($mbox, $mode, $increment) + { + $a_mailbox_cache = FALSE; + $mailbox = $mbox ? $mbox : $this->mailbox; + $mode = strtoupper($mode); + + $a_mailbox_cache = $this->get_cache('messagecount'); + + if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment)) + return FALSE; + + // add incremental value to messagecount + $a_mailbox_cache[$mailbox][$mode] += $increment; + + // write back to cache + $this->update_cache('messagecount', $a_mailbox_cache); + + return TRUE; + } + + + // remove messagecount of a specific mailbox from cache + function _clear_messagecount($mbox='') + { + $a_mailbox_cache = FALSE; + $mailbox = $mbox ? $mbox : $this->mailbox; + + $a_mailbox_cache = $this->get_cache('messagecount'); + + if (is_array($a_mailbox_cache[$mailbox])) + { + unset($a_mailbox_cache[$mailbox]); + $this->update_cache('messagecount', $a_mailbox_cache); + } + } + + + function _parse_address_list($str) + { + $a = $this->_explode_quoted_string(',', $str); + $result = array(); + + foreach ($a as $key => $val) + { + $val = str_replace("\"<", "\" <", $val); + $sub_a = $this->_explode_quoted_string(' ', $val); + + foreach ($sub_a as $k => $v) + { + if ((strpos($v, '@') > 0) && (strpos($v, '.') > 0)) + $result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v)); + else + $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v)); + } + + if (empty($result[$key]['name'])) + $result[$key]['name'] = $result[$key]['address']; + + $result[$key]['name'] = $this->decode_header($result[$key]['name']); + } + + return $result; + } + + + function _explode_quoted_string($delimiter, $string) + { + $quotes = explode("\"", $string); + foreach ($quotes as $key => $val) + if (($key % 2) == 1) + $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]); + + $string = implode("\"", $quotes); + + $result = explode($delimiter, $string); + foreach ($result as $key => $val) + $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]); + + return $result; + } + } + + + + + +function quoted_printable_encode($input="", $line_max=76, $space_conv=false) + { + $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + $lines = preg_split("/(?:\r\n|\r|\n)/", $input); + $eol = "\r\n"; + $escape = "="; + $output = ""; + + while( list(, $line) = each($lines)) + { + //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary + $linlen = strlen($line); + $newline = ""; + for($i = 0; $i < $linlen; $i++) + { + $c = substr( $line, $i, 1 ); + $dec = ord( $c ); + if ( ( $i == 0 ) && ( $dec == 46 ) ) // convert first point in the line into =2E + { + $c = "=2E"; + } + if ( $dec == 32 ) + { + if ( $i == ( $linlen - 1 ) ) // convert space at eol only + { + $c = "=20"; + } + else if ( $space_conv ) + { + $c = "=20"; + } + } + else if ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) // always encode "\t", which is *not* required + { + $h2 = floor($dec/16); + $h1 = floor($dec%16); + $c = $escape.$hex["$h2"].$hex["$h1"]; + } + + if ( (strlen($newline) + strlen($c)) >= $line_max ) // CRLF is not counted + { + $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay + $newline = ""; + // check if newline first character will be point or not + if ( $dec == 46 ) + { + $c = "=2E"; + } + } + $newline .= $c; + } // end of for + $output .= $newline.$eol; + } // end of while + + return trim($output); + } + +?> diff --git a/program/include/rcube_mysql.inc b/program/include/rcube_mysql.inc new file mode 100644 index 000000000..bcffe5e2b --- /dev/null +++ b/program/include/rcube_mysql.inc @@ -0,0 +1,186 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/rcube_mysql.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | MySQL wrapper class that implements PHP MySQL functions | + | See http://www.php.net/manual/en/ref.mysql.php | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +class rcube_mysql + { + var $db_link; + var $db_host = 'localhost'; + var $db_name = ''; + var $db_user = ''; + var $db_pass = ''; + var $a_query_results = array('dummy'); + var $last_res_id = 0; + + + // PHP 5 constructor + function __construct($db_name='', $user='', $pass='', $host='localhost') + { + $this->db_host = $host; + $this->db_name = $db_name; + $this->db_user = $user; + $this->db_pass = $pass; + } + + // PHP 4 compatibility + function rcube_mysql($db_name='', $user='', $pass='', $host='localhost') + { + $this->__construct($db_name, $user, $pass, $host); + } + + + function connect() + { + $this->db_link = mysql_connect($this->db_host, $this->db_user, $this->db_pass); + + if (!$this->db_link) + { + raise_error(array('code' => 500, + 'type' => 'mysql', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Can't connect to database"), TRUE, FALSE); + return FALSE; + } + + return TRUE; + } + + + function select_db($name) + { + $this->db_name = $name; + + if ($this->db_link) + mysql_select_db($name, $this->db_link); + } + + + function query($query) + { + // establish a connection + if (!$this->db_link) + { + if (!$this->connect()) + return FALSE; + } + + $sql_result = mysql_db_query($this->db_name, $query, $this->db_link); + return $this->_add_result($sql_result, $query); + } + + + function num_rows($res_id=NULL) + { + if (!$this->db_link) + return FALSE; + + $sql_result = $this->_get_result($res_id); + + if ($sql_result) + return mysql_num_rows($sql_result); + else + return FALSE; + } + + + function affected_rows() + { + if (!$this->db_link) + return FALSE; + + return mysql_affected_rows($this->db_link); + } + + + function insert_id() + { + if (!$this->db_link) + return FALSE; + + return mysql_insert_id($this->db_link); + } + + + function fetch_assoc($res_id=NULL) + { + $sql_result = $this->_get_result($res_id); + + if ($sql_result) + return mysql_fetch_assoc($sql_result); + else + return FALSE; + } + + + function seek($res_id=NULL, $row=0) + { + $sql_result = $this->_get_result($res_id); + + if ($sql_result) + return mysql_data_seek($sql_result, $row); + else + return FALSE; + } + + + + function _add_result($res, $query) + { + // sql error occured + if ($res===FALSE) + { + $sql_error = mysql_error($this->db_link); + raise_error(array('code' => 500, + 'type' => 'mysql', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => $sql_error."; QUERY: ".preg_replace('/[\r\n]+\s*/', ' ', $query)), TRUE, FALSE); + + return FALSE; + } + else + { + $res_id = sizeof($this->a_query_results); + $this->a_query_results[$res_id] = $res; + $this->last_res_id = $res_id; + + return $res_id; + } + } + + + function _get_result($res_id) + { + if ($res_id===NULL) + $res_id = $this->last_res_id; + + if ($res_id && isset($this->a_query_results[$res_id])) + return $this->a_query_results[$res_id]; + else + return FALSE; + } + + } + + +?>
\ No newline at end of file diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc new file mode 100644 index 000000000..2c0b0517b --- /dev/null +++ b/program/include/rcube_shared.inc @@ -0,0 +1,1417 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | rcube_shared.inc | + | | + | This file is part of the RoundCube PHP suite | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | 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 $scripts = array(); + + 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 $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 add_script($script, $position='head') + { + if (!isset($this->scripts[$position])) + $this->scripts[$position] = ''; + + $this->scripts[$position] .= "\n$script"; + } + + + function set_title() + { + + } + + + function write($templ='', $base_path='') + { + $output = trim($templ); + + // set default page title + if (!strlen($this->title)) + $this->title = 'RoundCube|Mail'; + + // replace specialchars in content + $__page_title = rep_specialchars_output($this->title, 'html', 'show', FALSE); + $__page_header = $__page_body = $__page_footer = ''; + + + // 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 (strlen($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 (strlen($this->scripts['foot'])) + $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']); + + + $__page_header .= $this->css->show(); + + + // find page header + if($hpos = strpos(strtolower($output), '</head>')) + $__page_header .= "\n"; + else + { + if (!is_numeric($hpos)) + $hpos = strpos(strtolower($output), '<body'); + if (!is_numeric($hpos) && ($hpos = strpos(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 = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output)); + else + $output = $__page_header . $output; + + + // find page body + if($bpos = strpos(strtolower($output), '<body')) + { + while($output[$bpos]!='>') $bpos++; + $bpos++; + } + else + $bpos = strpos(strtolower($output), '</head>')+7; + + // add page body + if($bpos && $__page_body) + $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output)); + + + // find and add page footer + if(($fpos = strpos(strtolower($output), '</body>')) || ($fpos = strpos(strtolower($output), '</html>'))) + $output = substr($output,0,$fpos) . "$__page_footer\n" . substr($output,$fpos,strlen($output)); + else + $output .= "\n$__page_footer"; + + + // reset those global vars + $__page_header = $__page_footer = ''; + + + // correct absolute pathes in images and other tags + $output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output); + + print $output; + } + + + 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 = rep_specialchars_output($value, 'html', 'replace', 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 (strlen($value)) + $value = rep_specialchars_output($value, 'html', 'replace', 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 = ((strlen($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'), + strlen($option['value']) ? sprintf($value_str, $option['value']) : '', + $selected, + rep_specialchars_output($option['text'], 'html', 'replace', 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() + { + global $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; + static $sa_text_data, $s_language; + + // 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 = strlen($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/labels.inc'); + @include($INSTALL_PATH.'program/localization/en/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); + else + return $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()) + header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+2600000)." GMT"); + } + + +// replace specials characters to a specific encoding type +function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) + { + global $OUTPUT_TYPE; + static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table; + + if (!$enctype) + $enctype = $GLOBALS['OUTPUT_TYPE']; + + // convert nbsps back to normal spaces if not html + if ($enctype!='html') + $str = str_replace(chr(160), ' ', $str); + + + // encode for plaintext + if ($enctype=='text') + return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); + + // encode for HTML output + if ($enctype=='html') + { + if (!$html_encode_arr) + { + $html_encode_arr = get_html_translation_table(HTML_ENTITIES); + $html_encode_arr["?"] = '–'; + $html_encode_arr[chr(128)] = '€'; + unset($html_encode_arr['?']); + unset($html_encode_arr['&']); + } + + $ltpos = strpos($str, '<'); + $encode_arr = $html_encode_arr; + + // don't replace quotes and html tags + if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) + { + unset($encode_arr['"']); + unset($encode_arr['<']); + unset($encode_arr['>']); + } + else if ($mode=='remove') + $str = strip_tags($str); + + $out = strtr($str, $encode_arr); + + return $newlines ? nl2br($out) : $out; + } + + + if ($enctype=='url') + return rawurlencode($str); + + + // if the replace tables for RTF, XML and JS are not yet defined + if (!$js_rep_table) + { + for ($c=160; $c<256; $c++) // can be increased to support more charsets + { + $hex = dechex($c); + $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); + $rtf_rep_table[Chr($c)] = "\\'$hex"; + $xml_rep_table[Chr($c)] = "&#$c;"; + } + + $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); + $xml_rep_table['"'] = '"'; + } + + // encode for RTF + if ($enctype=='xml') + return strtr($str, $xml_rep_table); + + // encode for javascript use + if ($enctype=='js') + return preg_replace(array("/\r\n/", '/"/', "/'/"), array('\n', '\"', "\'"), strtr($str, $js_rep_table)); + + // encode for RTF + if ($enctype=='rtf') + return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); + + // no encoding given -> return original string + return $str; + } + + +function decode_specialchars($input, $charset='') + { + $charset = strtolower($charset); + + if (strcasecmp($charset, 'utf-8')==0) + return utf8_decode($input); + else if ($charset=="koi8-r") + return convert_cyr_string($input, 'k', 'w'); + else if ($charset=="iso8859-5") + return convert_cyr_string($input, 'i', 'w'); + else if ($charset=="x-cp866") + return convert_cyr_string($input, 'a', 'w'); + else if ($charset=="x-mac-cyrillic") + return convert_cyr_string($input, 'm', 'w'); + + return $input; + } + + + +// 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)) + { + $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_numeric($value)) || $type=='int') && 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; + } + + +function show_bytes($numbytes) + { + if ($numbytes > 1024) + return sprintf('%d KB', round($numbytes/1024)); + else + return sprintf('%d B', $numbytes); + } + + +// 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; + } + + + +?>
\ No newline at end of file diff --git a/program/include/session.inc b/program/include/session.inc new file mode 100644 index 000000000..c68b3dba9 --- /dev/null +++ b/program/include/session.inc @@ -0,0 +1,154 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/session.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide database supported session management | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +function sess_open($save_path, $session_name) + { + return TRUE; + } + + + +function sess_close() + { + return TRUE; + } + + +// read session data +function sess_read($key) + { + global $DB, $SESS_CHANGED; + + $sql_result = $DB->query(sprintf("SELECT vars, UNIX_TIMESTAMP(changed) AS changed + FROM %s + WHERE sess_id='%s'", + get_table_name('session'), + $key)); + + if ($sql_arr = $DB->fetch_assoc($sql_result)) + { + $SESS_CHANGED = $sql_arr['changed']; + + if (strlen($sql_arr['vars'])) + return $sql_arr['vars']; + } + + return FALSE; + } + + +// save session data +function sess_write($key, $vars) + { + global $DB; + + $sql_result = $DB->query(sprintf("SELECT 1 + FROM %s + WHERE sess_id='%s'", + get_table_name('session'), + $key)); + + if ($DB->num_rows($sql_result)) + { + session_decode($vars); + $DB->query(sprintf("UPDATE %s + SET vars='%s', + changed=NOW() + WHERE sess_id='%s'", + get_table_name('session'), + $vars, + $key)); + } + else + { + $DB->query(sprintf("INSERT INTO %s + (sess_id, vars, created, changed) + VALUES ('%s', '%s', NOW(), NOW())", + get_table_name('session'), + $key, + $vars)); + } + + return TRUE; + } + + +// handler for session_destroy() +function sess_destroy($key) + { + global $DB; + + $DB->query(sprintf("DELETE FROM %s + WHERE sess_id='%s'", + get_table_name('session'), + $key)); + + // also delete session entries in cache table + $DB->query(sprintf("DELETE FROM %s + WHERE session_id='%s'", + get_table_name('cache'), + $key)); + + return TRUE; + } + + +// garbage collecting function +function sess_gc($maxlifetime) + { + global $DB; + + // get all expired sessions + $sql_result = $DB->query(sprintf("SELECT sess_id + FROM %s + WHERE UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(created) > %d", + get_table_name('session'), + $maxlifetime)); + + $a_exp_sessions = array(); + while ($sql_arr = $DB->fetch_assoc($sql_result)) + $a_exp_sessions[] = $sql_arr['sess_id']; + + + if (sizeof($a_exp_sessions)) + { + // delete session records + $DB->query(sprintf("DELETE FROM %s + WHERE sess_id IN ('%s')", + get_table_name('session'), + join("','", $a_exp_sessions))); + + // also delete session cache records + $DB->query(sprintf("DELETE FROM %s + WHERE session_id IN ('%s')", + get_table_name('cache'), + join("','", $a_exp_sessions))); + } + + return TRUE; + } + + +// set custom functions for PHP session management +session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy', 'sess_gc'); + +?> diff --git a/program/js/app.js b/program/js/app.js new file mode 100644 index 000000000..d8afc0e64 --- /dev/null +++ b/program/js/app.js @@ -0,0 +1,2388 @@ +/* + +-----------------------------------------------------------------------+ + | RoundCube Webmail Client Script | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | All rights reserved. | + | | + | Modified: 2005/08/19 (tbr) | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + + +var rcube_webmail_client; + +function rcube_webmail() + { + this.env = new Object(); + this.buttons = new Object(); + this.gui_objects = new Object(); + this.commands = new Object(); + this.selection = new Array(); + + // create public reference to myself + rcube_webmail_client = this; + this.ref = 'rcube_webmail_client'; + + // webmail client settings + this.dblclick_time = 600; + this.message_time = 5000; + this.mbox_expression = new RegExp('[^0-9a-z\-_]', 'gi'); + this.env.blank_img = 'skins/default/images/blank.gif'; + + // mimetypes supported by the browser (default settings) + this.mimetypes = new Array('text/plain', 'text/html', 'text/xml', + 'image/jpeg', 'image/gif', 'image/png', + 'application/x-javascript', 'application/pdf', + 'application/x-shockwave-flash'); + + + // set environment variable + this.set_env = function(name, value) + { + //if (!this.busy) + this.env[name] = value; + }; + + // add a button to the button list + this.register_button = function(command, id, type, act, sel, over) + { + if (!this.buttons[command]) + this.buttons[command] = new Array(); + + var button_prop = {id:id, type:type}; + if (act) button_prop.act = act; + if (sel) button_prop.sel = sel; + if (over) button_prop.over = over; + + this.buttons[command][this.buttons[command].length] = button_prop; + }; + + + // register a specific gui object + this.gui_object = function(name, id) + { + this.gui_objects[name] = id; + }; + + + // initialize webmail client + this.init = function() + { + this.task = this.env.task; + + // check browser + if (!(bw.dom && ((bw.ie && bw.vendver>=5.5 && !bw.opera) || (bw.mz && bw.vendver>=1) || (bw.safari && bw.vendver>=125) || (bw.opera && bw.vendver>=8)))) + { + location.href = this.env.comm_path+'&_action=error&_code=0x199'; + return; + } + + // find all registered gui objects + for (var n in this.gui_objects) + this.gui_objects[n] = rcube_find_object(this.gui_objects[n]); + + // tell parent window that this frame is loaded + if (this.env.framed && parent.rcmail && parent.rcmail.set_busy) + parent.rcmail.set_busy(false); + + // enable general commands + this.enable_command('logout', 'mail', 'addressbook', 'settings', true); + + switch (this.task) + { + case 'mail': + var msg_list = this.gui_objects.messagelist; + if (msg_list) + { + this.init_messagelist(msg_list); + this.enable_command('markread', true); + } + + // enable mail commands + this.enable_command('list', 'compose', 'add-contact', true); + + if (this.env.action=='show') + { + this.enable_command('show', 'reply', 'forward', 'moveto', 'delete', 'viewsource', 'print', 'load-attachment', true); + if (this.env.next_uid) + this.enable_command('nextmessage', true); + if (this.env.prev_uid) + this.enable_command('previousmessage', true); + } + + if (this.env.action=='show' && this.env.blockedobjects) + { + if (this.gui_objects.remoteobjectsmsg) + this.gui_objects.remoteobjectsmsg.style.display = 'block'; + this.enable_command('load-images', true); + } + + if (this.env.action=='compose') + this.enable_command('add-attachment', 'send-attachment', 'send', true); + + if (this.env.messagecount) + this.enable_command('select-all', 'select-none', true); + + this.set_page_buttons(); + + // focus this window + window.focus(); + + // init message compose form + if (this.env.action=='compose') + this.init_messageform(); + + // show printing dialog + if (this.env.action=='print') + window.print(); + + break; + + + case 'addressbook': + var contacts_list = this.gui_objects.contactslist; + if (contacts_list) + this.init_contactslist(contacts_list); + + this.set_page_buttons(); + + if (this.env.cid) + this.enable_command('show', 'edit', true); + + if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) + this.enable_command('save', true); + + this.enable_command('list', 'add', true); + break; + + + case 'settings': + this.enable_command('preferences', 'identities', 'save', 'folders', true); + + if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity') + this.enable_command('edit', 'add', 'delete', true); + + if (this.env.action=='edit-identity' || this.env.action=='add-identity') + this.enable_command('save', true); + + if (this.env.action=='folders') + this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'delete-folder', true); + + var identities_list = this.gui_objects.identitieslist; + if (identities_list) + this.init_identitieslist(identities_list); + + break; + + case 'login': + var input_user = rcube_find_object('_user'); + var input_pass = rcube_find_object('_pass'); + if (input_user && input_user.value=='') + input_user.focus(); + else if (input_pass) + input_pass.focus(); + + this.enable_command('login', true); + break; + + default: + break; + } + + + // enable basic commands + this.enable_command('logout', true); + + // disable browser's contextmenus + //document.oncontextmenu = function(){ return false; } + + // flag object as complete + this.loaded = true; + + // show message + if (this.pending_message) + this.display_message(this.pending_message[0], this.pending_message[1]); + }; + + + // get all message rows from HTML table and init each row + this.init_messagelist = function(msg_list) + { + if (msg_list && msg_list.tBodies[0]) + { + this.message_rows = new Array(); + + var row; + for(var r=0; r<msg_list.tBodies[0].childNodes.length; r++) + { + row = msg_list.tBodies[0].childNodes[r]; + //row = msg_list.tBodies[0].rows[r]; + this.init_message_row(row); + } + } + + // alias to common rows array + this.list_rows = this.message_rows; + }; + + + // make references in internal array and set event handlers + this.init_message_row = function(row) + { + var uid, msg_icon; + + if (String(row.id).match(/rcmrow([0-9]+)/)) + { + uid = RegExp.$1; + row.uid = uid; + + this.message_rows[uid] = {id:row.id, obj:row, + classname:row.className, + unread:this.env.messages[uid] ? this.env.messages[uid].unread : null, + replied:this.env.messages[uid] ? this.env.messages[uid].replied : null}; + + // set eventhandlers to table row + row.onmousedown = function(e){ return rcube_webmail_client.drag_row(e, this.uid); }; + row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.uid); }; + + // set eventhandler to message icon + if ((msg_icon = row.cells[0].childNodes[0]) && row.cells[0].childNodes[0].nodeName=='IMG') + { + msg_icon.id = 'msgicn_'+uid; + msg_icon._row = row; + msg_icon.onmousedown = function(e) { rcube_webmail_client.command('markread', this); }; + + // get message icon and save original icon src + this.message_rows[uid].icon = msg_icon; + } + } + }; + + + // init message compose form: set focus and eventhandlers + this.init_messageform = function() + { + if (!this.gui_objects.messageform) + return false; + + //this.messageform = this.gui_objects.messageform; + var input_to = rcube_find_object('_to'); + var input_cc = rcube_find_object('_cc'); + var input_bcc = rcube_find_object('_bcc'); + var input_replyto = rcube_find_object('_replyto'); + var input_subject = rcube_find_object('_subject'); + var input_message = rcube_find_object('_message'); + + // init live search events + if (input_to) + this.init_address_input_events(input_to); + if (input_cc) + this.init_address_input_events(input_cc); + if (input_bcc) + this.init_address_input_events(input_bcc); + + if (input_to && input_to.value=='') + input_to.focus(); + else if (input_subject && input_subject.value=='') + input_subject.focus(); + else if (input_message) + this.set_caret2start(input_message); // input_message.focus(); + }; + + + this.init_address_input_events = function(obj) + { + var handler = function(e){ return rcube_webmail_client.ksearch_keypress(e,this); }; + var handler2 = function(e){ return rcube_webmail_client.ksearch_blur(e,this); }; + + if (bw.safari) + { + obj.addEventListener('keydown', handler, false); + // obj.addEventListener('blur', handler2, false); + } + else if (bw.mz) + { + obj.addEventListener('keypress', handler, false); + obj.addEventListener('blur', handler2, false); + } + else if (bw.ie) + { + obj.onkeydown = handler; + //obj.attachEvent('onkeydown', handler); + // obj.attachEvent('onblur', handler2, false); + } + + obj.setAttribute('autocomplete', 'off'); + }; + + + + // get all contact rows from HTML table and init each row + this.init_contactslist = function(contacts_list) + { + if (contacts_list && contacts_list.tBodies[0]) + { + this.contact_rows = new Array(); + + var row; + for(var r=0; r<contacts_list.tBodies[0].childNodes.length; r++) + { + row = contacts_list.tBodies[0].childNodes[r]; + this.init_table_row(row, 'contact_rows'); + } + } + + // alias to common rows array + this.list_rows = this.contact_rows; + + if (this.env.cid) + this.select(this.env.cid); + }; + + + // make references in internal array and set event handlers + this.init_table_row = function(row, array_name) + { + var cid; + + if (String(row.id).match(/rcmrow([0-9]+)/)) + { + cid = RegExp.$1; + row.cid = cid; + + this[array_name][cid] = {id:row.id, + obj:row, + classname:row.className}; + + // set eventhandlers to table row + row.onmousedown = function(e) { rcube_webmail_client.in_selection_before=this.cid; return false; }; // fake for drag handler + row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.cid); }; + } + }; + + + // get all contact rows from HTML table and init each row + this.init_identitieslist = function(identities_list) + { + if (identities_list && identities_list.tBodies[0]) + { + this.identity_rows = new Array(); + + var row; + for(var r=0; r<identities_list.tBodies[0].childNodes.length; r++) + { + row = identities_list.tBodies[0].childNodes[r]; + this.init_table_row(row, 'identity_rows'); + } + } + + // alias to common rows array + this.list_rows = this.identity_rows; + + if (this.env.iid) + this.select(this.env.iid); + }; + + + + /*********************************************************/ + /********* client command interface *********/ + /*********************************************************/ + + + // execute a specific command on the web client + this.command = function(command, props, obj) + { + if (obj && obj.blur) + obj.blur(); + + if (this.busy) + return false; + + // command not supported or allowed + if (!this.commands[command]) + { + // pass command to parent window + if (this.env.framed && parent.rcmail && parent.rcmail.command) + parent.rcmail.command(command, props); + + return false; + } + + // process command + switch (command) + { + case 'login': + if (this.gui_objects.loginform) + this.gui_objects.loginform.submit(); + break; + + case 'logout': + location.href = this.env.comm_path+'&_action=logout'; + break; + + // commands to switch task + case 'mail': + case 'addressbook': + case 'settings': + this.switch_task(command); + break; + + + // misc list commands + case 'list': + if (this.task=='mail') + this.list_mailbox(props); + else if (this.task=='addressbook') + this.list_contacts(); + break; + + case 'nextpage': + this.list_page('next'); + break; + + case 'previouspage': + this.list_page('prev'); + break; + + + // common commands used in multiple tasks + case 'show': + if (this.task=='mail') + { + var uid = this.get_single_uid(); + if (uid && (!this.env.uid || uid != this.env.uid)) + this.show_message(uid); + } + else if (this.task=='addressbook') + { + var cid = props ? props : this.get_single_cid(); + if (cid && !(this.env.action=='show' && cid==this.env.cid)) + this.load_contact(cid, 'show'); + } + break; + + case 'add': + if (this.task=='addressbook') + this.load_contact(0, 'add'); + else if (this.task=='settings') + { + this.clear_selection(); + this.load_identity(0, 'add-identity'); + } + break; + + case 'edit': + var cid; + if (this.task=='addressbook' && (cid = this.get_single_cid())) + this.load_contact(cid, 'edit'); + else if (this.task=='settings' && props) + this.load_identity(props, 'edit-identity'); + break; + + case 'save-identity': + case 'save': + if (this.gui_objects.editform) + this.gui_objects.editform.submit(); + break; + + case 'delete': + // mail task + if (this.task=='mail' && this.env.trash_mailbox && String(this.env.mailbox).toLowerCase()!=String(this.env.trash_mailbox).toLowerCase()) + this.move_messages(this.env.trash_mailbox); + else if (this.task=='mail') + this.delete_messages(); + // addressbook task + else if (this.task=='addressbook') + this.delete_contacts(); + // user settings task + else if (this.task=='settings') + this.delete_identity(); + break; + + + // mail task commands + case 'move': + case 'moveto': + this.move_messages(props); + break; + + case 'markread': + if (props && !props._row) + break; + + var uid; + var flag = 'read'; + + if (props._row.uid) + { + uid = props._row.uid; + this.dont_select = true; + + // toggle read/unread + if (!this.message_rows[uid].unread) + flag = 'unread'; + } + + this.mark_message(flag, uid); + break; + + case 'load-images': + if (this.env.uid) + this.show_message(this.env.uid, true); + break; + + case 'load-attachment': + var url = this.env.comm_path+'&_action=get&_mbox='+this.env.mailbox+'&_uid='+this.env.uid+'&_part='+props.part; + + // open attachment in frame if it's of a supported mimetype + if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0) + { + this.attachment_win = window.open(url+'&_frame=1', 'rcubemailattachment'); + if (this.attachment_win) + { + setTimeout(this.ref+'.attachment_win.focus()', 10); + break; + } + } + + location.href = url; + break; + + case 'select-all': + this.select_all(props); + break; + + case 'select-none': + this.clear_selection(); + break; + + case 'nextmessage': + if (this.env.next_uid) + location.href = this.env.comm_path+'&_action=show&_uid='+this.env.next_uid+'&_mbox='+this.env.mailbox; + break; + + case 'previousmessage': + if (this.env.prev_uid) + location.href = this.env.comm_path+'&_action=show&_uid='+this.env.prev_uid+'&_mbox='+this.env.mailbox; + break; + + case 'compose': + var url = this.env.comm_path+'&_action=compose'; + + // modify url if we're in addressbook + if (this.task=='addressbook') + { + url = this.get_task_url('mail', url); + var a_cids = new Array(); + + // use contact_id passed as command parameter + if (props) + a_cids[a_cids.length] = props; + + // get selected contacts + else + { + for (var n=0; n<this.selection.length; n++) + a_cids[a_cids.length] = this.selection[n]; + } + + if (a_cids.length) + url += '&_to='+a_cids.join(','); + else + break; + } + else if (props) + url += '&_to='+props; + + this.set_busy(true); + location.href = url; + break; + + case 'send': + if (!this.gui_objects.messageform) + break; + + // check input fields + var input_to = rcube_find_object('_to'); + var input_subject = rcube_find_object('_subject'); + var input_message = rcube_find_object('_message'); + + if (input_to.value!='' && input_message.value!='') + { + this.set_busy(true, 'sendingmessage'); + var form = this.gui_objects.messageform; + form.submit(); + } + + break; + + case 'add-attachment': + this.show_attachment_form(true); + + case 'send-attachment': + this.upload_file(props) + break; + + case 'reply': + var uid; + if (uid = this.get_single_uid()) + { + this.set_busy(true); + location.href = this.env.comm_path+'&_action=compose&_reply_uid='+uid+'&_mbox='+escape(this.env.mailbox); + } + break; + + case 'forward': + var uid; + if (uid = this.get_single_uid()) + { + this.set_busy(true); + location.href = this.env.comm_path+'&_action=compose&_forward_uid='+uid+'&_mbox='+escape(this.env.mailbox); + } + break; + + case 'print': + var uid; + if (uid = this.get_single_uid()) + { + this.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+escape(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : '')); + if (this.printwin) + setTimeout(this.ref+'.printwin.focus()', 20); + } + break; + + case 'viewsource': + var uid; + if (uid = this.get_single_uid()) + { + this.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+this.env.uid+'&_mbox='+escape(this.env.mailbox)); + if (this.sourcewin) + setTimeout(this.ref+'.sourcewin.focus()', 20); + } + break; + + case 'add-contact': + this.add_contact(props); + break; + + + // user settings commands + case 'preferences': + location.href = this.env.comm_path; + break; + + case 'identities': + location.href = this.env.comm_path+'&_action=identities'; + break; + + case 'delete-identity': + this.delete_identity(); + + case 'folders': + location.href = this.env.comm_path+'&_action=folders'; + break; + + case 'subscribe': + this.subscribe_folder(props); + break; + + case 'unsubscribe': + this.unsubscribe_folder(props); + break; + + case 'create-folder': + this.create_folder(props); + break; + + case 'delete-folder': + if (confirm('Do you really want to delete this folder?')) + this.delete_folder(props); + break; + + } + + return obj ? false : true; + }; + + + // set command enabled or disabled + this.enable_command = function() + { + var args = this.enable_command.arguments; + if(!args.length) return -1; + + var command; + var enable = args[args.length-1]; + + for(var n=0; n<args.length-1; n++) + { + command = args[n]; + this.commands[command] = enable; + this.set_button(command, (enable ? 'act' : 'pas')); + } + }; + + + this.set_busy = function(a, message) + { + if (a && message) + this.display_message('Loading...', 'loading', true); + else if (!a && this.busy) + this.hide_message(); + + this.busy = a; + //document.body.style.cursor = a ? 'wait' : 'default'; + + if (this.gui_objects.editform) + this.lock_form(this.gui_objects.editform, a); + }; + + + this.switch_task = function(task) + { + if (this.task===task) + return; + + this.set_busy(true); + location.href = this.get_task_url(task); + }; + + + this.get_task_url = function(task, url) + { + if (!url) + url = this.env.comm_path; + + return url.replace(/_task=[a-z]+/, '_task='+task); + }; + + + /*********************************************************/ + /********* event handling methods *********/ + /*********************************************************/ + + + // onmouseup handler for mailboxlist item + this.mbox_mouse_up = function(mbox) + { + if (this.drag_active) + this.command('moveto', mbox); + else + this.command('list', mbox); + + return false; + }; + + + // onmousedown-handler of message list row + this.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; + + if (!this.in_selection_before) + { + var shift = this.check_shiftkey(e); + this.select(id, shift); + } + + if (this.selection.length) + { + this.drag_start = true; + document.onmousemove = function(e){ return rcube_webmail_client.drag_mouse_move(e); }; + document.onmouseup = function(e){ return rcube_webmail_client.drag_mouse_up(e); }; + } + + return false; + }; + + + // onmouseup-handler of message list row + this.click_row = function(e, id) + { + var shift = this.check_shiftkey(e); + + // don't do anything (another action processed before) + if (this.dont_select) + { + this.dont_select = false; + return false; + } + + if (!this.drag_active && this.in_selection_before==id) + this.select(id, (shift && this.task!='settings')); + + this.drag_start = false; + this.in_selection_before = false; + + // row was double clicked + if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && !shift) + { + this.show_message(id); + return false; + } + else if (this.task=='addressbook') + { + if (this.selection.length==1 && this.env.contentframe) + this.load_contact(this.selection[0], 'show', true); + else if (this.task=='addressbook' && this.list_rows && this.list_rows[id].clicked) + { + this.load_contact(id, 'show'); + return false; + } + else if (this.env.contentframe) + { + var elm = document.getElementById(this.env.contentframe); + elm.style.visibility = 'hidden'; + } + } + else if (this.task=='settings') + { + if (this.selection.length==1) + this.command('edit', this.selection[0]); + } + + this.list_rows[id].clicked = true; + setTimeout(this.ref+'.list_rows['+id+'].clicked=false;', this.dblclick_time); + + return false; + }; + + + + + /*********************************************************/ + /********* (message) list functionality *********/ + /*********************************************************/ + + + // select/unselect a row + this.select = function(id, multiple) + { + var selected = false + + if (this.list_rows[id] && !multiple) + { + this.clear_selection(); + this.selection[0] = id; + this.list_rows[id].obj.className += ' selected'; + selected = true; + } + + else if (this.list_rows[id]) + { + if (!this.in_selection(id)) // select row + { + this.selection[this.selection.length] = id; + this.set_classname(this.list_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.list_rows[id].obj, 'selected', false); + } + + selected = (this.selection.length==1); + } + + // enable/disable commands for message + if (this.task=='mail') + { + this.enable_command('show', 'reply', 'forward', 'print', selected); + this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false); + } + else if (this.task=='addressbook') + { + this.enable_command('edit', /*'print',*/ selected); + this.enable_command('delete', 'compose', this.selection.length>0 ? true : false); + } + }; + + + this.clear_selection = function() + { + for(var n=0; n<this.selection.length; n++) + if (this.list_rows[this.selection[n]]) + this.set_classname(this.list_rows[this.selection[n]].obj, 'selected', false); + + this.selection = new Array(); + }; + + + // check if given id is part of the current selection + this.in_selection = function(id) + { + for(var n in this.selection) + if (this.selection[n]==id) + return true; + + return false; + }; + + + // select each row in list + this.select_all = function(filter) + { + if (!this.list_rows || !this.list_rows.length) + return false; + + // reset selection first + this.clear_selection(); + + for (var n in this.list_rows) + if (!filter || this.list_rows[n][filter]==true) + this.select(n, true); + }; + + + // when user doble-clicks on a row + this.show_message = function(id, safe) + { + var add_url = ''; + var target = window; + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) + { + target = window.frames[this.env.contentframe]; + add_url = '&_framed=1'; + } + + if (safe) + add_url = '&_safe=1'; + + if (id) + { + this.set_busy(true); + target.location.href = this.env.comm_path+'&_action=show&_uid='+id+'&_mbox='+escape(this.env.mailbox)+add_url; + } + }; + + + + // list a specific page + this.list_page = function(page) + { + if (page=='next') + page = this.env.current_page+1; + if (page=='prev' && this.env.current_page>1) + page = this.env.current_page-1; + + if (page > 0 && page <= this.env.pagecount) + { + this.env.current_page = page; + + if (this.task=='mail') + this.list_mailbox(this.env.mailbox, page); + else if (this.task=='addressbook') + this.list_contacts(page); + } + }; + + + // list messages of a specific mailbox + this.list_mailbox = function(mbox, page) + { + var add_url = ''; + var target = window; + + if (!mbox) + mbox = this.env.mailbox; + + // set page=1 if changeing to another mailbox + if (!page && mbox != this.env.mailbox) + { + page = 1; + this.env.current_page = page; + this.clear_selection(); + } + + if (this.env.mailbox!=mbox) + this.select_mailbox(mbox); + + // load message list remotely + if (this.gui_objects.messagelist) + { + this.list_mailbox_remote(mbox, page); + return; + } + + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) + { + target = window.frames[this.env.contentframe]; + add_url = '&_framed=1'; + } + + // load message list to target frame/window + if (mbox) + { + this.set_busy(true, 'loading'); + target.location.href = this.env.comm_path+'&_mbox='+escape(mbox)+(page ? '&_page='+page : '')+add_url; + } + }; + + + // send remote request to load message list + this.list_mailbox_remote = function(mbox, page) + { + // clear message list + var table = this.gui_objects.messagelist; + var tbody = document.createElement('TBODY'); + table.insertBefore(tbody, table.tBodies[0]); + table.removeChild(table.tBodies[1]); + + this.message_rows = new Array(); + this.list_rows = this.message_rows; + + // send request to server + var url = '_mbox='+escape(mbox)+(page ? '&_page='+page : ''); + this.set_busy(true, 'loading'); + this.http_request('list', url); + }; + + + // move selected messages to the specified mailbox + this.move_messages = function(mbox) + { + // exit if no mailbox specified or if selection is empty + if (!mbox || !(this.selection.length || this.env.uid) || mbox==this.env.mailbox) + return; + + var a_uids = new Array(); + + if (this.env.uid) + a_uids[a_uids.length] = this.env.uid; + else + { + var id; + for (var n=0; n<this.selection.length; n++) + { + id = this.selection[n]; + a_uids[a_uids.length] = id; + + // 'remove' message row from list (just hide it) + if (this.message_rows[id].obj) + this.message_rows[id].obj.style.display = 'none'; + } + } + + // show wait message + if (this.env.action=='show') + this.set_busy(true, 'movingmessage'); + + // send request to server + this.http_request('moveto', '_uid='+a_uids.join(',')+'&_mbox='+escape(this.env.mailbox)+'&_target_mbox='+escape(mbox)+'&_from='+(this.env.action ? this.env.action : '')); + }; + + + // delete selected messages from the current mailbox + this.delete_messages = function() + { + // exit if no mailbox specified or if selection is empty + if (!(this.selection.length || this.env.uid)) + return; + + var a_uids = new Array(); + + if (this.env.uid) + a_uids[a_uids.length] = this.env.uid; + else + { + var id; + for (var n=0; n<this.selection.length; n++) + { + id = this.selection[n]; + a_uids[a_uids.length] = id; + + // 'remove' message row from list (just hide it) + if (this.message_rows[id].obj) + this.message_rows[id].obj.style.display = 'none'; + } + } + + // send request to server + this.http_request('delete', '_uid='+a_uids.join(',')+'&_mbox='+escape(this.env.mailbox)+'&_from='+(this.env.action ? this.env.action : '')); + }; + + + // set a specific flag to one or more messages + this.mark_message = function(flag, uid) + { + var a_uids = new Array(); + + if (uid) + a_uids[0] = uid; + else if (this.env.uid) + a_uids[0] = this.env.uid; + else + { + var id; + for (var n=0; n<this.selection.length; n++) + { + id = this.selection[n]; + a_uids[a_uids.length] = id; + + // 'remove' message row from list (just hide it) + if (this.message_rows[id].obj) + this.message_rows[id].obj.style.display = 'none'; + } + } + + // mark all message rows as read/unread + var icn_src; + for (var i=0; i<a_uids.length; i++) + { + uid = a_uids[i]; + if (this.message_rows[uid]) + { + this.message_rows[uid].unread = (flag=='unread' ? true : false); + + if (this.message_rows[uid].classname.indexOf('unread')<0 && this.message_rows[uid].unread) + { + this.message_rows[uid].classname += ' unread'; + if (!this.in_selection(uid)) + this.message_rows[uid].obj.className += ' unread'; + if (this.env.unreadicon) + icn_src = this.env.unreadicon; + } + else if (!this.message_rows[uid].unread) + { + this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*unread/, ''); + if (!this.in_selection(uid)) + this.message_rows[uid].obj.className = this.message_rows[uid].obj.className.replace(/\s*unread/, ''); + + if (this.message_rows[uid].replied && this.env.repliedicon) + icn_src = this.env.repliedicon; + else if (this.env.messageicon) + icn_src = this.env.messageicon; + } + + if (this.message_rows[uid].icon && icn_src) + this.message_rows[uid].icon.src = icn_src; + } + } + + // send request to server + this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag='+flag); + }; + + + + /*********************************************************/ + /********* message compose methods *********/ + /*********************************************************/ + + + this.show_attachment_form = function(a) + { + if (!this.gui_objects.uploadbox) + return false; + + var elm, list; + if (elm = this.gui_objects.uploadbox) + { + if (a && (list = this.gui_objects.attachmentlist)) + { + var pos = rcube_get_object_pos(list); + var left = pos.x; + var top = pos.y + list.offsetHeight + 10; + + elm.style.top = top+'px'; + elm.style.left = left+'px'; + } + + elm.style.visibility = a ? 'visible' : 'hidden'; + } + + // clear upload form + if (!a && this.gui_objects.attachmentform && this.gui_objects.attachmentform!=this.gui_objects.messageform) + this.gui_objects.attachmentform.reset(); + }; + + + // upload attachment file + this.upload_file = function(form) + { + if (!form) + return false; + + // get file input fields + var send = false; + for (var n=0; n<form.elements.length; n++) + if (form.elements[n].type=='file' && form.elements[n].value) + { + send = true; + break; + } + + // create hidden iframe and post upload form + if (send) + { + var ts = new Date().getTime(); + var frame_name = 'rcmupload'+ts; + + // have to do it this way for IE + // otherwise the form will be posted to a new window + if(document.all && !window.opera) + { + var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; + document.body.insertAdjacentHTML('BeforeEnd',html); + } + else // for standards-compilant browsers + { + var frame = document.createElement('IFRAME'); + frame.name = frame_name; + frame.width = 10; + frame.height = 10; + frame.style.visibility = 'hidden'; + document.body.appendChild(frame); + } + + form.target = frame_name; + form.action = this.env.comm_path+'&_action=upload'; + form.setAttribute('enctype', 'multipart/form-data'); + form.submit(); + } + + // set reference to the form object + this.gui_objects.attachmentform = form; + }; + + + // add file name to attachment list + // called from upload page + this.add2attachment_list = function(name) + { + if (!this.gui_objects.attachmentlist) + return false; + + var li = document.createElement('LI'); + li.innerHTML = name; + this.gui_objects.attachmentlist.appendChild(li); + }; + + + // send remote request to add a new contact + this.add_contact = function(value) + { + if (value) + this.http_request('addcontact', '_address='+value); + }; + + + /*********************************************************/ + /********* keyboard live-search methods *********/ + /*********************************************************/ + + + // handler for keyboard events on address-fields + this.ksearch_keypress = function(e, obj) + { + if (typeof(this.env.contacts)!='object' || !this.env.contacts.length) + return true; + + if (this.ksearch_timer) + clearTimeout(this.ksearch_timer); + + if (!e) + e = window.event; + + var highlight; + var key = e.keyCode ? e.keyCode : e.which; + + switch (key) + { + case 38: // key up + case 40: // key down + if (!this.ksearch_pane) + break; + + var dir = key==38 ? 1 : 0; + var next; + + highlight = document.getElementById('rcmksearchSelected'); + if (!highlight) + highlight = this.ksearch_pane.ul.firstChild; + + if (highlight && (next = dir ? highlight.previousSibling : highlight.nextSibling)) + { + highlight.removeAttribute('id'); + //highlight.removeAttribute('class'); + this.set_classname(highlight, 'selected', false); + } + + if (next) + { + next.setAttribute('id', 'rcmksearchSelected'); + this.set_classname(next, 'selected', true); + this.ksearch_selected = next._rcm_id; + } + + if (e.preventDefault) + e.preventDefault(); + return false; + + case 9: // tab + if(e.shiftKey) + break; + + case 13: // enter + if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value) + break; + + // get cursor pos + var inp_value = this.ksearch_input.value.toLowerCase(); + var cpos = this.get_caret_pos(this.ksearch_input); + var p = inp_value.lastIndexOf(this.ksearch_value, cpos); + + // replace search string with full address + var pre = this.ksearch_input.value.substring(0, p); + var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length); + var insert = this.env.contacts[this.ksearch_selected]+', '; + this.ksearch_input.value = pre + insert + end; + + //this.ksearch_input.value = this.ksearch_input.value.substring(0, p)+insert; + + // set caret to insert pos + cpos = p+insert.length; + if (this.ksearch_input.setSelectionRange) + this.ksearch_input.setSelectionRange(cpos, cpos); + + // hide ksearch pane + this.ksearch_hide(); + + if (e.preventDefault) + e.preventDefault(); + return false; + + case 27: // escape + this.ksearch_hide(); + break; + + } + + // start timer + this.ksearch_timer = setTimeout(this.ref+'.ksearch_get_results()', 200); + this.ksearch_input = obj; + + return true; + }; + + + // address search processor + this.ksearch_get_results = function() + { + var inp_value = this.ksearch_input ? this.ksearch_input.value : null; + if (inp_value===null) + return; + + // get string from current cursor pos to last comma + var cpos = this.get_caret_pos(this.ksearch_input); + var p = inp_value.lastIndexOf(',', cpos-1); + var q = inp_value.substring(p+1, cpos); + + // trim query string + q = q.replace(/(^\s+|\s+$)/g, '').toLowerCase(); + + if (!q.length || q==this.ksearch_value) + { + if (!q.length && this.ksearch_pane && this.ksearch_pane.visible) + this.ksearch_pane.show(0); + + return; + } + + this.ksearch_value = q; + + // start searching the contact list + var a_results = new Array(); + var a_result_ids = new Array(); + var c=0; + for (var i=0; i<this.env.contacts.length; i++) + { + if (this.env.contacts[i].toLowerCase().indexOf(q)>=0) + { + a_results[c] = this.env.contacts[i]; + a_result_ids[c++] = i; + + if (c==15) // limit search results + break; + } + } + + // display search results + if (c && a_results.length) + { + var p, ul, li; + + // create results pane if not present + if (!this.ksearch_pane) + { + ul = document.createElement('UL'); + this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000}); + this.ksearch_pane.elm.appendChild(ul); + this.ksearch_pane.ul = ul; + } + else + ul = this.ksearch_pane.ul; + + // remove all search results + ul.innerHTML = ''; + + // add each result line to list + for (i=0; i<a_results.length; i++) + { + li = document.createElement('LI'); + li.innerHTML = a_results[i].replace(/</, '<').replace(/>/, '>'); + li._rcm_id = a_result_ids[i]; + ul.appendChild(li); + } + + // check if last selected item is still in result list + if (this.ksearch_selected!==null) + { + p = find_in_array(this.ksearch_selected, a_result_ids); + if (p>=0 && ul.childNodes) + { + ul.childNodes[p].setAttribute('id', 'rcmksearchSelected'); + this.set_classname(ul.childNodes[p], 'selected', true); + } + else + this.ksearch_selected = null; + } + + // if no item selected, select the first one + if (this.ksearch_selected===null) + { + ul.firstChild.setAttribute('id', 'rcmksearchSelected'); + this.set_classname(ul.firstChild, 'selected', true); + this.ksearch_selected = a_result_ids[0]; + } + + // resize the containing layer to fit the list + //this.ksearch_pane.resize(ul.offsetWidth, ul.offsetHeight); + + // move the results pane right under the input box and make it visible + var pos = rcube_get_object_pos(this.ksearch_input); + this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight); + this.ksearch_pane.show(1); + } + // hide results pane + else + this.ksearch_hide(); + }; + + + this.ksearch_blur = function(e, obj) + { + if (this.ksearch_timer) + clearTimeout(this.ksearch_timer); + + this.ksearch_value = ''; + this.ksearch_input = null; + + this.ksearch_hide(); + }; + + + this.ksearch_hide = function() + { + this.ksearch_selected = null; + + if (this.ksearch_pane) + this.ksearch_pane.show(0); + }; + + + + /*********************************************************/ + /********* address book methods *********/ + /*********************************************************/ + + + this.list_contacts = function(page) + { + var add_url = ''; + var target = window; + + if (page && this.current_page==page) + return false; + + // load contacts remotely + if (this.gui_objects.contactslist) + { + this.list_contacts_remote(page); + return; + } + + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) + { + target = window.frames[this.env.contentframe]; + add_url = '&_framed=1'; + } + + this.set_busy(true, 'loading'); + location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url; + }; + + + // send remote request to load contacts list + this.list_contacts_remote = function(page) + { + // clear list + var table = this.gui_objects.contactslist; + var tbody = document.createElement('TBODY'); + table.insertBefore(tbody, table.tBodies[0]); + table.tBodies[1].style.display = 'none'; + + this.contact_rows = new Array(); + this.list_rows = this.contact_rows; + + // send request to server + var url = page ? '&_page='+page : ''; + this.set_busy(true, 'loading'); + this.http_request('list', url); + }; + + + // load contact record + this.load_contact = function(cid, action, framed) + { + var add_url = ''; + var target = window; + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) + { + add_url = '&_framed=1'; + target = window.frames[this.env.contentframe]; + document.getElementById(this.env.contentframe).style.visibility = 'inherit'; + } + else if (framed) + return false; + + //if (this.env.framed && add_url=='') + // add_url = '&_framed=1'; + + if (action && (cid || action=='add')) + { + this.set_busy(true); + target.location.href = this.env.comm_path+'&_action='+action+'&_cid='+cid+add_url; + } + }; + + + this.delete_contacts = function() + { + // exit if no mailbox specified or if selection is empty + if (!(this.selection.length || this.env.cid)) + return; + + var a_cids = new Array(); + + if (this.env.cid) + a_cids[a_cids.length] = this.env.cid; + else + { + var id; + for (var n=0; n<this.selection.length; n++) + { + id = this.selection[n]; + a_cids[a_cids.length] = id; + + // 'remove' row from list (just hide it) + if (this.contact_rows[id].obj) + this.contact_rows[id].obj.style.display = 'none'; + } + + // hide content frame if we delete the currently displayed contact + if (this.selection.length==1 && this.env.contentframe) + { + var elm = document.getElementById(this.env.contentframe); + elm.style.visibility = 'hidden'; + } + } + + // send request to server + this.http_request('delete', '_cid='+a_cids.join(',')+'&_from='+(this.env.action ? this.env.action : '')); + }; + + + // update a contact record in the list + this.update_contact_row = function(cid, cols_arr) + { + if (!this.contact_rows[cid] || !this.contact_rows[cid].obj) + return false; + + var row = this.contact_rows[cid].obj; + for (var c=0; c<cols_arr.length; c++) + if (row.cells[c]) + row.cells[c].innerHTML = cols_arr[c]; + + }; + + + + /*********************************************************/ + /********* user settings methods *********/ + /*********************************************************/ + + + // load contact record + this.load_identity = function(id, action) + { + if (action=='edit-identity' && (!id || id==this.env.iid)) + return; + + var add_url = ''; + var target = window; + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) + { + add_url = '&_framed=1'; + target = window.frames[this.env.contentframe]; + document.getElementById(this.env.contentframe).style.visibility = 'inherit'; + } + + if (action && (id || action=='add-identity')) + { + this.set_busy(true); + target.location.href = this.env.comm_path+'&_action='+action+'&_iid='+id+add_url; + } + }; + + + + this.delete_identity = function(id) + { + // exit if no mailbox specified or if selection is empty + if (!(this.selection.length || this.env.iid)) + return; + + if (!id) + id = this.env.iid ? this.env.iid : this.selection[0]; + +/* + // 'remove' row from list (just hide it) + if (this.identity_rows && this.identity_rows[id].obj) + { + this.clear_selection(); + this.identity_rows[id].obj.style.display = 'none'; + } +*/ + + // if (this.env.framed && id) + this.set_busy(true); + location.href = this.env.comm_path+'&_action=delete-identity&_iid='+id; + // else if (id) + // this.http_request('delete-identity', '_iid='+id); + }; + + + this.create_folder = function(name) + { + var form; + if ((form = this.gui_objects.editform) && form.elements['_folder_name']) + name = form.elements['_folder_name'].value; + + if (name) + this.http_request('create-folder', '_name='+escape(name)); + else if (form.elements['_folder_name']) + form.elements['_folder_name'].focus(); + }; + + + this.delete_folder = function(folder) + { + if (folder) + { + for (var id in this.env.subscriptionrows) + if (this.env.subscriptionrows[id]==folder) + break; + + var row; + if (id && (row = document.getElementById(id))) + row.style.display = 'none'; + + this.http_request('delete-folder', '_mboxes='+escape(folder)); + } + }; + + + this.subscribe_folder = function(folder) + { + var form; + if ((form = this.gui_objects.editform) && form.elements['_unsubscribed']) + this.change_subscription('_unsubscribed', '_subscribed', 'subscribe'); + else if (folder) + this.http_request('subscribe', '_mboxes='+escape(folder)); + }; + + + this.unsubscribe_folder = function(folder) + { + var form; + if ((form = this.gui_objects.editform) && form.elements['_subscribed']) + this.change_subscription('_subscribed', '_unsubscribed', 'unsubscribe'); + else if (folder) + this.http_request('unsubscribe', '_mboxes='+escape(folder)); + }; + + + this.change_subscription = function(from, to, action) + { + var form; + if (form = this.gui_objects.editform) + { + var a_folders = new Array(); + var list_from = form.elements[from]; + + for (var i=0; list_from && i<list_from.options.length; i++) + { + if (list_from.options[i] && list_from.options[i].selected) + { + a_folders[a_folders.length] = list_from.options[i].value; + list_from[i] = null; + i--; + } + } + + // yes, we have some folders selected + if (a_folders.length) + { + var list_to = form.elements[to]; + var index; + + for (var n=0; n<a_folders.length; n++) + { + index = list_to.options.length; + list_to[index] = new Option(a_folders[n]); + } + + this.http_request(action, '_mboxes='+escape(a_folders.join(','))); + } + } + + }; + + + // add a new folder to the subscription list by cloning a folder row + this.add_folder_row = function(name) + { + if (!this.gui_objects.subscriptionlist) + return false; + + var tbody = this.gui_objects.subscriptionlist.tBodies[0]; + var id = tbody.childNodes.length+1; + + // clone a table row + var row = this.clone_table_row(tbody.rows[0]); + row.id = 'rcmrow'+id; + tbody.appendChild(row); + + // add to folder/row-ID map + this.env.subscriptionrows[row.id] = name; + + // set folder name + row.cells[0].innerHTML = name; + if (row.cells[1].firstChild.tagName=='INPUT') + { + row.cells[1].firstChild.value = name; + row.cells[1].firstChild.checked = true; + } + if (row.cells[2].firstChild.tagName=='A') + row.cells[2].firstChild.onclick = new Function(this.ref+".command('delete-folder','"+name+"')"); + }; + + + // duplicate a specific table row + this.clone_table_row = function(row) + { + var cell, td; + var new_row = document.createElement('TR'); + for(var n=0; n<row.childNodes.length; n++) + { + cell = row.childNodes[n]; + td = document.createElement('TD'); + + if (cell.className) + td.className = cell.className; + if (cell.align) + td.setAttribute('align', cell.align); + + td.innerHTML = cell.innerHTML; + new_row.appendChild(td); + } + + return new_row; + }; + + + /*********************************************************/ + /********* GUI functionality *********/ + /*********************************************************/ + + + // eable/disable buttons for page shifting + this.set_page_buttons = function() + { + this.enable_command('nextpage', (this.env.pagecount > this.env.current_page)); + this.enable_command('previouspage', (this.env.current_page > 1)); + } + + + // set button to a specific state + this.set_button = function(command, state) + { + var a_buttons = this.buttons[command]; + var button, obj; + + if(!a_buttons || !a_buttons.length) + return; + + for(var n=0; n<a_buttons.length; n++) + { + button = a_buttons[n]; + obj = document.getElementById(button.id); + + // get default/passive setting of the button + if (obj && button.type=='image' && !button.status) + button.pas = obj._original_src ? obj._original_src : obj.src; + else if (obj && !button.status) + button.pas = String(obj.className); + + // set image according to button state + if (obj && button.type=='image' && button[state]) + { + button.status = state; + obj.src = button[state]; + } + // set class name according to button state + else if (obj && typeof(button[state])!='undefined') + { + button.status = state; + obj.className = button[state]; + } + // disable/enable input buttons + if (obj && button.type=='input') + { + button.status = state; + obj.disabled = !state; + } + } + }; + + + // mouse over button + this.button_over = function(command, id) + { + var a_buttons = this.buttons[command]; + var button, img; + + if(!a_buttons || !a_buttons.length) + return; + + for(var n=0; n<a_buttons.length; n++) + { + button = a_buttons[n]; + if(button.id==id && button.status=='act') + { + img = document.getElementById(button.id); + if (img && button.over) + img.src = button.over; + } + } + }; + + + // mouse out of button + this.button_out = function(command, id) + { + var a_buttons = this.buttons[command]; + var button, img; + + if(!a_buttons || !a_buttons.length) + return; + + for(var n=0; n<a_buttons.length; n++) + { + button = a_buttons[n]; + if(button.id==id && button.status=='act') + { + img = document.getElementById(button.id); + if (img && button.act) + img.src = button.act; + } + } + }; + + + // set/unset a specific class name + this.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; + }; + + + // display a specific alttext + this.alttext = function(text) + { + + }; + + + // display a system message + this.display_message = function(msg, type, hold) + { + if (!this.loaded) // save message in order to display after page loaded + { + this.pending_message = new Array(msg, type); + return true; + } + + if (!this.gui_objects.message) + return false; + + if (this.message_timer) + clearTimeout(this.message_timer); + + var cont = msg; + if (type) + cont = '<div class="'+type+'">'+cont+'</div>'; + + this.gui_objects.message.innerHTML = cont; + this.gui_objects.message.style.display = 'block'; + + if (!hold) + this.message_timer = setTimeout(this.ref+'.hide_message()', this.message_time); + }; + + + // make a message row disapear + this.hide_message = function() + { + if (this.gui_objects.message) + this.gui_objects.message.style.display = 'none'; + }; + + + // mark a mailbox as selected and set environment variable + this.select_mailbox = function(mbox) + { + if (this.gui_objects.mailboxlist) + { + var item, reg, text_obj; + var s_mbox = String(mbox).toLowerCase().replace(this.mbox_expression, ''); + var s_current = this.env.mailbox.toLowerCase().replace(this.mbox_expression, ''); + for (var n=0; n<this.gui_objects.mailboxlist.childNodes.length; n++) + { + item = this.gui_objects.mailboxlist.childNodes[n]; + if (item.className && item.className.indexOf('mailbox '+s_mbox+' ')>=0) + this.set_classname(item, 'selected', true); + else if (item.className && item.className.indexOf('mailbox '+s_current)>=0) + this.set_classname(item, 'selected', false); + } + } + + this.env.mailbox = mbox; + }; + + + // create a table row in the message list + this.add_message_row = function(uid, cols, flags, attachment) + { + if (!this.gui_objects.messagelist || !this.gui_objects.messagelist.tBodies[0]) + return false; + + var tbody = this.gui_objects.messagelist.tBodies[0]; + var rowcount = tbody.rows.length; + var even = rowcount%2; + + this.env.messages[uid] = {replied:flags.replied?1:0, + unread:flags.unread?1:0}; + + var row = document.createElement('TR'); + row.id = 'rcmrow'+uid; + row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : ''); + + if (this.in_selection(uid)) + row.className += ' selected'; + + var icon = flags.unread && this.env.unreadicon ? this.env.unreadicon : + (flags.replied && this.env.repliedicon ? this.env.repliedicon : this.env.messageicon); + + var col = document.createElement('TD'); + col.className = 'icon'; + col.innerHTML = icon ? '<img src="'+icon+'" alt="" border="0" />' : ''; + row.appendChild(col); + + // add each submitted col + for (var c in cols) + { + col = document.createElement('TD'); + col.className = String(c).toLowerCase(); + col.innerHTML = cols[c]; + row.appendChild(col); + } + + col = document.createElement('TD'); + col.className = 'icon'; + col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" border="0" />' : ''; + row.appendChild(col); + + tbody.appendChild(row); + this.init_message_row(row); + }; + + + // replace content of row count display + this.set_rowcount = function(text) + { + if (this.gui_objects.countdisplay) + this.gui_objects.countdisplay.innerHTML = text; + + // update page navigation buttons + this.set_page_buttons(); + }; + + + // update the mailboxlist + this.set_unread_count = function(mbox, count) + { + if (!this.gui_objects.mailboxlist) + return false; + + mbox = String(mbox).toLowerCase().replace(this.mbox_expression, ''); + + var item, reg, text_obj; + for (var n=0; n<this.gui_objects.mailboxlist.childNodes.length; n++) + { + item = this.gui_objects.mailboxlist.childNodes[n]; + + if (item.className && item.className.indexOf('mailbox '+mbox)>=0) + { + // set new text + text_obj = item.firstChild; + reg = /\s+\([0-9]+\)$/i; + + if (count && text_obj.innerHTML.match(reg)) + text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+count+')'); + else if (count) + text_obj.innerHTML += ' ('+count+')'; + else + text_obj.innerHTML = text_obj.innerHTML.replace(reg, ''); + + // set the right classes + this.set_classname(item, 'unread', count>0 ? true : false); + break; + } + } + }; + + + // add row to contacts list + this.add_contact_row = function(cid, cols) + { + if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0]) + return false; + + var tbody = this.gui_objects.contactslist.tBodies[0]; + var rowcount = tbody.rows.length; + var even = rowcount%2; + + var row = document.createElement('TR'); + row.id = 'rcmrow'+cid; + row.className = 'contact '+(even ? 'even' : 'odd'); + + if (this.in_selection(cid)) + row.className += ' selected'; + + // add each submitted col + for (var c in cols) + { + col = document.createElement('TD'); + col.className = String(c).toLowerCase(); + col.innerHTML = cols[c]; + row.appendChild(col); + } + + tbody.appendChild(row); + this.init_table_row(row, 'contact_rows'); + }; + + + + /********************************************************/ + /********* drag & drop methods *********/ + /********************************************************/ + + + this.drag_mouse_move = function(e) + { + if (this.drag_start) + { + 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, subject, obj; + for(var n=0; n<this.selection.length; n++) + { + if (n>12) // only show 12 lines + { + names += '...'; + break; + } + + if (this.message_rows[this.selection[n]].obj) + { + obj = this.message_rows[this.selection[n]].obj; + subject = ''; + + for(c=0; c<obj.childNodes.length; c++) + if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3) + { + subject = obj.childNodes[c].firstChild.data; + names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />'; + } + } + } + + this.draglayer.write(names); + this.draglayer.show(1); + } + + var pos = this.get_mouse_pos(e); + this.draglayer.move(pos.x+20, pos.y-5); + + this.drag_start = false; + this.drag_active = true; + + return false; + }; + + + this.drag_mouse_up = function() + { + document.onmousemove = null; + + if (this.draglayer && this.draglayer.visible) + this.draglayer.show(0); + + this.drag_active = false; + + return false; + }; + + + + /********************************************************/ + /********* remote request methods *********/ + /********************************************************/ + + + // send a http request to the server + this.http_request = function(action, querystring) + { + if (window.XMLHttpRequest) + this.request_obj = new XMLHttpRequest(); + else if (window.ActiveXObject) + this.request_obj = new ActiveXObject("Microsoft.XMLHTTP"); + else + { + + } + + querystring += '&_remote=1'; + + // add timestamp to request url to avoid cacheing problems in Safari + if (bw.safari) + querystring += '&_ts='+(new Date().getTime()); + + // send request + if (this.request_obj) + { + // prompt('request', this.env.comm_path+'&_action='+escape(action)+'&'+querystring); + console('HTTP request: '+this.env.comm_path+'&_action='+escape(action)+'&'+querystring); + this.set_busy(true); + this.request_action = action; + this.request_obj.onreadystatechange = function(){ rcube_webmail_client.http_response(); }; + this.request_obj.open('GET', this.env.comm_path+'&_action='+escape(action)+'&'+querystring); + this.request_obj.send(null); + } + }; + + + // handle http response + this.http_response = function() + { + if (this.request_obj.readyState == 4) // || this.request_obj.readyState == 2) + { + var ctype = this.request_obj.getResponseHeader('Content-Type'); + if (ctype) + ctype = String(ctype).toLowerCase(); + + this.set_busy(false); + + console(this.request_obj.responseText); + + // if we get javascript code from server -> execute it + if (this.request_obj.responseText && (ctype=='text/javascript' || ctype=='application/x-javascript')) + eval(this.request_obj.responseText); + + // process the response data according to the sent action + switch (this.request_action) + { + case 'delete': + case 'moveto': + if (this.env.action=='show') + this.command('list'); + break; + + case 'list': + this.enable_command('select-all', 'select-none', this.env.messagecount ? true : false); + break; + } + } + }; + + + /********************************************************/ + /********* helper methods *********/ + /********************************************************/ + + // check if we're in show mode or if we have a unique selection + // and return the message uid + this.get_single_uid = function() + { + return this.env.uid ? this.env.uid : (this.selection.length==1 ? this.selection[0] : null); + }; + + // same as above but for contacts + this.get_single_cid = function() + { + return this.env.cid ? this.env.cid : (this.selection.length==1 ? this.selection[0] : null); + }; + + + // check if Shift-key is pressed on event + this.check_shiftkey = function(e) + { + if(!e && window.event) + e = window.event; + + if(bw.linux && bw.ns4 && e.modifiers) + return true; + else if((bw.ns4 && e.modifiers & Event.SHIFT_MASK) || (e && e.shiftKey)) + return true; + else + return false; + } + + + this.get_mouse_pos = function(e) + { + if(!e) e = window.event; + var mX = (e.pageX) ? e.pageX : e.clientX; + var mY = (e.pageY) ? e.pageY : e.clientY; + + if(document.body && document.all) + { + mX += document.body.scrollLeft; + mY += document.body.scrollTop; + } + + return { x:mX, y:mY }; + }; + + + this.get_caret_pos = function(obj) + { + if (typeof(obj.selectionEnd)!='undefined') + return obj.selectionEnd; + + else if (document.selection && document.selection.createRange) + { + var range = document.selection.createRange(); + if (range.parentElement()!=obj) + return 0; + + var gm = range.duplicate(); + if (obj.tagName=='TEXTAREA') + gm.moveToElementText(obj); + else + gm.expand('textedit'); + + gm.setEndPoint('EndToStart', range); + var p = gm.text.length; + + return p<=obj.value.length ? p : -1; + } + + else + return obj.value.length; + }; + + + this.set_caret2start = function(obj) + { + if (obj.createTextRange) + { + var range = obj.createTextRange(); + range.collapse(true); + range.select(); + } + else if (obj.setSelectionRange) + obj.setSelectionRange(0,0); + + obj.focus(); + }; + + + // set all fields of a form disabled + this.lock_form = function(form, lock) + { + if (!form || !form.elements) + return; + + var type; + for (var n=0; n<form.elements.length; n++) + { + type = form.elements[n]; + if (type=='hidden') + continue; + + form.elements[n].disabled = lock; + } + }; + + } // end object rcube_webmail + + + + +function console(str) + { + if (document.debugform && document.debugform.console) + document.debugform.console.value += str+'\n--------------------------------------\n'; + } + + +// set onload handler +window.onload = function(e) + { + if (window.rcube_webmail_client) + rcube_webmail_client.init(); + }; diff --git a/program/js/common.js b/program/js/common.js new file mode 100644 index 000000000..0947bbd1f --- /dev/null +++ b/program/js/common.js @@ -0,0 +1,343 @@ +/* + +-----------------------------------------------------------------------+ + | RoundCube common js library | + | | + | This file is part of the RoundCube web development suite | + | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | All rights reserved. | + | | + | Modified: 19.08.2005 (tbr) | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + + +// default browsercheck +function roundcube_browser() + { + this.ver = parseFloat(navigator.appVersion); + this.appver = navigator.appVersion; + this.agent = navigator.userAgent; + this.name = navigator.appName; + this.vendor = navigator.vendor ? navigator.vendor : ''; + this.vendver = navigator.vendorSub ? parseFloat(navigator.vendorSub) : 0; + this.product = navigator.product ? navigator.product : ''; + this.platform = String(navigator.platform).toLowerCase(); + this.lang = (navigator.language) ? navigator.language.substring(0,2) : + (navigator.browserLanguage) ? navigator.browserLanguage.substring(0,2) : + (navigator.systemLanguage) ? navigator.systemLanguage.substring(0,2) : 'en'; + + this.win = (this.platform.indexOf('win')>=0) ? true : false; + this.mac = (this.platform.indexOf('mac')>=0) ? true : false; + this.linux = (this.platform.indexOf('linux')>=0) ? true : false; + this.unix = (this.platform.indexOf('unix')>=0) ? true : false; + + this.dom = document.getElementById ? true : false; + this.dom2 = (document.addEventListener && document.removeEventListener); + + this.ie = (document.all) ? true : false; + this.ie4 = (this.ie && !this.dom); + this.ie5 = (this.dom && this.appver.indexOf('MSIE 5')>0); + this.ie6 = (this.dom && this.appver.indexOf('MSIE 6')>0); + + this.mz = (this.dom && this.ver>=5); // (this.dom && this.product=='Gecko') + this.ns = ((this.ver<5 && this.name=='Netscape') || (this.ver>=5 && this.vendor.indexOf('Netscape')>=0)); + this.ns4 = (this.ns && parseInt(this.ver)==4); + this.ns6 = (this.ns && parseInt(this.vendver)==6); // (this.mz && this.ns) ? true : false; + this.ns7 = (this.ns && parseInt(this.vendver)==7); // this.agent.indexOf('Netscape/7')>0); + this.safari = this.agent.toLowerCase().indexOf('safari')>0; + this.konq = (this.agent.toLowerCase().indexOf('konqueror')>0); + + this.opera = (window.opera) ? true : false; + this.opera5 = (this.opera5 && this.agent.indexOf('Opera 5')>0) ? true : false; + this.opera6 = (this.opera && this.agent.indexOf('Opera 6')>0) ? true : false; + this.opera7 = (this.opera && this.agent.indexOf('Opera 7')>0) ? true : false; + + if(this.opera && window.RegExp) + this.vendver = (/opera(\s|\/)([0-9\.]+)/i.test(navigator.userAgent)) ? parseFloat(RegExp.$2) : -1; + else if(!this.vendver && this.safari) + this.vendver = (/safari\/([0-9]+)/i.test(this.agent)) ? parseInt(RegExp.$1) : 0; + else if((!this.vendver && this.mz) || this.agent.indexOf('Camino')>0) + this.vendver = (/rv:([0-9\.]+)/.test(this.agent)) ? parseFloat(RegExp.$1) : 0; + else if(this.ie && window.RegExp) + this.vendver = (/msie\s+([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0; + + // get real language out of safari's user agent + if(this.safari && (/;\s+([a-z]{2})-[a-z]{2}\)/i.test(this.agent))) + this.lang = RegExp.$1; + + this.dhtml = ((this.ie4 && this.win) || this.ie5 || this.ie6 || this.ns4 || this.mz); + this.layers = this.ns4; // (document.layers); + this.div = (this.ie4 || this.dom); + this.vml = (this.win && this.ie && this.dom && !this.opera); + this.linkborder = (this.ie || this.mz); + this.rollover = (this.ver>=4 || (this.ns && this.ver>=3)); // (document.images) ? true : false; + this.pngalpha = (this.mz || (this.opera && this.vendver>=6) || (this.ie && this.mac && this.vendver>=5) || + (this.ie && this.win && this.vendver>=5.5) || this.safari); + this.opacity = (this.mz || (this.ie && this.vendver>=5.5 && !this.opera) || (this.safari && this.vendver>=100)); + this.cookies = navigator.cookieEnabled; + } + + + + +var rcube_layer_objects = new Array(); + +function rcube_layer(id, attributes) + { + this.name = id; + + // create a new layer in the current document + this.create = function(arg) + { + var l = (arg.x) ? arg.x : 0; + var t = (arg.y) ? arg.y : 0; + var w = arg.width; + var h = arg.height; + var z = arg.zindex; + var vis = arg.vis; + var parent = arg.parent; + var obj; + + obj = document.createElement('DIV'); + with(obj) + { + id = this.name; + with(style) + { + position = 'absolute'; + visibility = (vis) ? (vis==2) ? 'inherit' : 'visible' : 'hidden'; + left = l+'px'; + top = t+'px'; + if(w) width = w+'px'; + if(h) height = h+'px'; + if(z) zIndex = z; + } + } + + if(parent) parent.appendChild(obj); + else document.body.appendChild(obj); + + this.elm = obj; + }; + + + // create new layer + if(attributes!=null) + { + this.create(attributes); + this.name = this.elm.id; + } + else // just refer to the object + this.elm = document.getElementById(id); + + + if(!this.elm) + return false; + + + // ********* layer object properties ********* + + this.css = this.elm.style; + this.event = this.elm; + this.width = this.elm.offsetWidth; + this.height = this.elm.offsetHeight; + this.x = parseInt(this.elm.offsetLeft); + this.y = parseInt(this.elm.offsetTop); + this.visible = (this.css.visibility=='visible' || this.css.visibility=='show' || this.css.visibility=='inherit') ? true : false; + + this.id = rcube_layer_objects.length; + this.obj = 'rcube_layer_objects['+this.id+']'; + rcube_layer_objects[this.id] = this; + + + // ********* layer object methods ********* + + + // move the layer to a specific position + this.move = function(x, y) + { + this.x = x; + this.y = y; + this.css.left = Math.round(this.x)+'px'; + this.css.top = Math.round(this.y)+'px'; + } + + + // move the layer for a specific step + this.shift = function(x,y) + { + x = Math.round(x*100)/100; + y = Math.round(y*100)/100; + this.move(this.x+x, this.y+y); + } + + + // change the layers width and height + this.resize = function(w,h) + { + this.css.width = w+'px'; + this.css.height = h+'px'; + this.width = w; + this.height = h; + } + + + // cut the layer (top,width,height,left) + this.clip = function(t,w,h,l) + { + this.css.clip='rect('+t+' '+w+' '+h+' '+l+')'; + this.clip_height = h; + this.clip_width = w; + } + + + // show or hide the layer + this.show = function(a) + { + if(a==1) + { + this.css.visibility = 'visible'; + this.visible = true; + } + else if(a==2) + { + this.css.visibility = 'inherit'; + this.visible = true; + } + else + { + this.css.visibility = 'hidden'; + this.visible = false; + } + } + + + // write new content into a Layer + this.write = function(cont) + { + this.elm.innerHTML = cont; + } + + + // set the given color to the layer background + this.set_bgcolor = function(c) + { + if(!c || c=='#') + c = 'transparent'; + + this.css.backgroundColor = c; + } + + + // set the opacity of a layer to the given ammount (in %) + this.set_opacity = function(v) + { + if(!bw.opacity) + return; + + var op = v<=1 ? Math.round(v*100) : parseInt(v); + + if(bw.ie) + this.css.filter = 'alpha(opacity:'+op+')'; + else if(bw.safari) + { + this.css.opacity = op/100; + this.css.KhtmlOpacity = op/100; + } + else if(bw.mz) + this.css.MozOpacity = op/100; + } + } + + + +// find a value in a specific array and returns the index +function find_in_array() + { + var args = find_in_array.arguments; + if(!args.length) return -1; + + var haystack = typeof(args[0])=='object' ? args[0] : args.length>1 && typeof(args[1])=='object' ? args[1] : new Array(); + var needle = typeof(args[0])!='object' ? args[0] : args.length>1 && typeof(args[1])!='object' ? args[1] : ''; + var nocase = args.length==3 ? args[2] : false; + + if(!haystack.length) return -1; + + for(var i=0; i<haystack.length; i++) + if(nocase && haystack[i].toLowerCase()==needle.toLowerCase()) + return i; + else if(haystack[i]==needle) + return i; + + return -1; + } + + +// get any type of html objects by id/name +function rcube_find_object(id, d) + { + var n, f, obj, e; + if(!d) d = document; + + if(d.getElementsByName && (e = d.getElementsByName(id))) + obj = e[0]; + if(!obj && d.getElementById) + obj = d.getElementById(id); + if(!obj && d.all) + obj = d.all[id]; + + if(!obj && d.images.length) + obj = d.images[id]; + + if(!obj && d.forms.length) + for(f=0; f<d.forms.length; f++) + { + if(d.forms[f].name == id) + obj = d.forms[f]; + else if(d.forms[f].elements[id]) + obj = d.forms[f].elements[id]; + } + + if(!obj && d.layers) + { + if(d.layers[id]) obj = d.layers[id]; + for(n=0; !obj && n<d.layers.length; n++) + obj = nex_get_object(id, d.layers[n].document); + } + + return obj; + } + + +// return the absolute position of an object within the document +function rcube_get_object_pos(obj) + { + if(typeof(obj)=='string') + obj = nex_get_object(obj); + + if(!obj) return {x:0, y:0}; + + var iX = (bw.layers) ? obj.x : obj.offsetLeft; + var iY = (bw.layers) ? obj.y : obj.offsetTop; + + if(bw.ie || bw.mz) + { + var elm = obj.offsetParent; + while(elm && elm!=null) + { + iX += elm.offsetLeft; + iY += elm.offsetTop; + elm = elm.offsetParent; + } + } + + if(bw.mac && bw.ie5) iX += document.body.leftMargin; + if(bw.mac && bw.ie5) iY += document.body.topMargin; + + return {x:iX, y:iY}; + } + +var bw = new roundcube_browser();
\ No newline at end of file diff --git a/program/lib/Mail/mime.php b/program/lib/Mail/mime.php new file mode 100644 index 000000000..830352c42 --- /dev/null +++ b/program/lib/Mail/mime.php @@ -0,0 +1,733 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2002-2003 Richard Heyes | +// | Copyright (c) 2003-2005 The PHP Group | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o Redistributions in binary form must reproduce the above copyright | +// | notice, this list of conditions and the following disclaimer in the | +// | documentation and/or other materials provided with the distribution.| +// | o The names of the authors may not be used to endorse or promote | +// | products derived from this software without specific prior written | +// | permission. | +// | | +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Richard Heyes <richard@phpguru.org> | +// | Tomas V.V.Cox <cox@idecnet.com> (port to PEAR) | +// +-----------------------------------------------------------------------+ +// +// $Id$ + +require_once('PEAR.php'); +require_once('Mail/mimePart.php'); + +/** + * Mime mail composer class. Can handle: text and html bodies, embedded html + * images and attachments. + * Documentation and examples of this class are avaible here: + * http://pear.php.net/manual/ + * + * @notes This class is based on HTML Mime Mail class from + * Richard Heyes <richard@phpguru.org> which was based also + * in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it> and + * Sascha Schumann <sascha@schumann.cx> + * + * Function _encodeHeaders() changed by Thomas Bruederli <roundcube@gmail.com> + * in order to be read correctly by Google Gmail + * + * @author Richard Heyes <richard.heyes@heyes-computing.net> + * @author Tomas V.V.Cox <cox@idecnet.com> + * @package Mail + * @access public + */ +class Mail_mime +{ + /** + * Contains the plain text part of the email + * @var string + */ + var $_txtbody; + /** + * Contains the html part of the email + * @var string + */ + var $_htmlbody; + /** + * contains the mime encoded text + * @var string + */ + var $_mime; + /** + * contains the multipart content + * @var string + */ + var $_multipart; + /** + * list of the attached images + * @var array + */ + var $_html_images = array(); + /** + * list of the attachements + * @var array + */ + var $_parts = array(); + /** + * Build parameters + * @var array + */ + var $_build_params = array(); + /** + * Headers for the mail + * @var array + */ + var $_headers = array(); + /** + * End Of Line sequence (for serialize) + * @var string + */ + var $_eol; + + + /** + * Constructor function + * + * @access public + */ + function Mail_mime($crlf = "\r\n") + { + $this->_setEOL($crlf); + $this->_build_params = array( + 'text_encoding' => '7bit', + 'html_encoding' => 'quoted-printable', + '7bit_wrap' => 998, + 'html_charset' => 'ISO-8859-1', + 'text_charset' => 'ISO-8859-1', + 'head_charset' => 'ISO-8859-1' + ); + } + + /** + * Wakeup (unserialize) - re-sets EOL constant + * + * @access private + */ + function __wakeup() + { + $this->_setEOL($this->_eol); + } + + /** + * Accessor function to set the body text. Body text is used if + * it's not an html mail being sent or else is used to fill the + * text/plain part that emails clients who don't support + * html should show. + * + * @param string $data Either a string or + * the file name with the contents + * @param bool $isfile If true the first param should be treated + * as a file name, else as a string (default) + * @param bool $append If true the text or file is appended to + * the existing body, else the old body is + * overwritten + * @return mixed true on success or PEAR_Error object + * @access public + */ + function setTXTBody($data, $isfile = false, $append = false) + { + if (!$isfile) { + if (!$append) { + $this->_txtbody = $data; + } else { + $this->_txtbody .= $data; + } + } else { + $cont = $this->_file2str($data); + if (PEAR::isError($cont)) { + return $cont; + } + if (!$append) { + $this->_txtbody = $cont; + } else { + $this->_txtbody .= $cont; + } + } + return true; + } + + /** + * Adds a html part to the mail + * + * @param string $data Either a string or the file name with the + * contents + * @param bool $isfile If true the first param should be treated + * as a file name, else as a string (default) + * @return mixed true on success or PEAR_Error object + * @access public + */ + function setHTMLBody($data, $isfile = false) + { + if (!$isfile) { + $this->_htmlbody = $data; + } else { + $cont = $this->_file2str($data); + if (PEAR::isError($cont)) { + return $cont; + } + $this->_htmlbody = $cont; + } + + return true; + } + + /** + * Adds an image to the list of embedded images. + * + * @param string $file The image file name OR image data itself + * @param string $c_type The content type + * @param string $name The filename of the image. + * Only use if $file is the image data + * @param bool $isfilename Whether $file is a filename or not + * Defaults to true + * @return mixed true on success or PEAR_Error object + * @access public + */ + function addHTMLImage($file, $c_type='application/octet-stream', + $name = '', $isfilename = true) + { + $filedata = ($isfilename === true) ? $this->_file2str($file) + : $file; + if ($isfilename === true) { + $filename = ($name == '' ? basename($file) : basename($name)); + } else { + $filename = basename($name); + } + if (PEAR::isError($filedata)) { + return $filedata; + } + $this->_html_images[] = array( + 'body' => $filedata, + 'name' => $filename, + 'c_type' => $c_type, + 'cid' => md5(uniqid(time())) + ); + return true; + } + + /** + * Adds a file to the list of attachments. + * + * @param string $file The file name of the file to attach + * OR the file data itself + * @param string $c_type The content type + * @param string $name The filename of the attachment + * Only use if $file is the file data + * @param bool $isFilename Whether $file is a filename or not + * Defaults to true + * @return mixed true on success or PEAR_Error object + * @access public + */ + function addAttachment($file, $c_type = 'application/octet-stream', + $name = '', $isfilename = true, + $encoding = 'base64') + { + $filedata = ($isfilename === true) ? $this->_file2str($file) + : $file; + if ($isfilename === true) { + // Force the name the user supplied, otherwise use $file + $filename = (!empty($name)) ? $name : $file; + } else { + $filename = $name; + } + if (empty($filename)) { + return PEAR::raiseError( + 'The supplied filename for the attachment can\'t be empty' + ); + } + $filename = basename($filename); + if (PEAR::isError($filedata)) { + return $filedata; + } + + $this->_parts[] = array( + 'body' => $filedata, + 'name' => $filename, + 'c_type' => $c_type, + 'encoding' => $encoding + ); + return true; + } + + /** + * Get the contents of the given file name as string + * + * @param string $file_name path of file to process + * @return string contents of $file_name + * @access private + */ + function &_file2str($file_name) + { + if (!is_readable($file_name)) { + return PEAR::raiseError('File is not readable ' . $file_name); + } + if (!$fd = fopen($file_name, 'rb')) { + return PEAR::raiseError('Could not open ' . $file_name); + } + $cont = fread($fd, filesize($file_name)); + fclose($fd); + return $cont; + } + + /** + * Adds a text subpart to the mimePart object and + * returns it during the build process. + * + * @param mixed The object to add the part to, or + * null if a new object is to be created. + * @param string The text to add. + * @return object The text mimePart object + * @access private + */ + function &_addTextPart(&$obj, $text) + { + $params['content_type'] = 'text/plain'; + $params['encoding'] = $this->_build_params['text_encoding']; + $params['charset'] = $this->_build_params['text_charset']; + if (is_object($obj)) { + return $obj->addSubpart($text, $params); + } else { + return new Mail_mimePart($text, $params); + } + } + + /** + * Adds a html subpart to the mimePart object and + * returns it during the build process. + * + * @param mixed The object to add the part to, or + * null if a new object is to be created. + * @return object The html mimePart object + * @access private + */ + function &_addHtmlPart(&$obj) + { + $params['content_type'] = 'text/html'; + $params['encoding'] = $this->_build_params['html_encoding']; + $params['charset'] = $this->_build_params['html_charset']; + if (is_object($obj)) { + return $obj->addSubpart($this->_htmlbody, $params); + } else { + return new Mail_mimePart($this->_htmlbody, $params); + } + } + + /** + * Creates a new mimePart object, using multipart/mixed as + * the initial content-type and returns it during the + * build process. + * + * @return object The multipart/mixed mimePart object + * @access private + */ + function &_addMixedPart() + { + $params['content_type'] = 'multipart/mixed'; + return new Mail_mimePart('', $params); + } + + /** + * Adds a multipart/alternative part to a mimePart + * object (or creates one), and returns it during + * the build process. + * + * @param mixed The object to add the part to, or + * null if a new object is to be created. + * @return object The multipart/mixed mimePart object + * @access private + */ + function &_addAlternativePart(&$obj) + { + $params['content_type'] = 'multipart/alternative'; + if (is_object($obj)) { + return $obj->addSubpart('', $params); + } else { + return new Mail_mimePart('', $params); + } + } + + /** + * Adds a multipart/related part to a mimePart + * object (or creates one), and returns it during + * the build process. + * + * @param mixed The object to add the part to, or + * null if a new object is to be created + * @return object The multipart/mixed mimePart object + * @access private + */ + function &_addRelatedPart(&$obj) + { + $params['content_type'] = 'multipart/related'; + if (is_object($obj)) { + return $obj->addSubpart('', $params); + } else { + return new Mail_mimePart('', $params); + } + } + + /** + * Adds an html image subpart to a mimePart object + * and returns it during the build process. + * + * @param object The mimePart to add the image to + * @param array The image information + * @return object The image mimePart object + * @access private + */ + function &_addHtmlImagePart(&$obj, $value) + { + $params['content_type'] = $value['c_type']; + $params['encoding'] = 'base64'; + $params['disposition'] = 'inline'; + $params['dfilename'] = $value['name']; + $params['cid'] = $value['cid']; + $obj->addSubpart($value['body'], $params); + } + + /** + * Adds an attachment subpart to a mimePart object + * and returns it during the build process. + * + * @param object The mimePart to add the image to + * @param array The attachment information + * @return object The image mimePart object + * @access private + */ + function &_addAttachmentPart(&$obj, $value) + { + $params['content_type'] = $value['c_type']; + $params['encoding'] = $value['encoding']; + $params['disposition'] = 'attachment'; + $params['dfilename'] = $value['name']; + $obj->addSubpart($value['body'], $params); + } + + /** + * Builds the multipart message from the list ($this->_parts) and + * returns the mime content. + * + * @param array Build parameters that change the way the email + * is built. Should be associative. Can contain: + * text_encoding - What encoding to use for plain text + * Default is 7bit + * html_encoding - What encoding to use for html + * Default is quoted-printable + * 7bit_wrap - Number of characters before text is + * wrapped in 7bit encoding + * Default is 998 + * html_charset - The character set to use for html. + * Default is iso-8859-1 + * text_charset - The character set to use for text. + * Default is iso-8859-1 + * head_charset - The character set to use for headers. + * Default is iso-8859-1 + * @return string The mime content + * @access public + */ + function &get($build_params = null) + { + if (isset($build_params)) { + while (list($key, $value) = each($build_params)) { + $this->_build_params[$key] = $value; + } + } + + if (!empty($this->_html_images) AND isset($this->_htmlbody)) { + foreach ($this->_html_images as $value) { + $regex = '#src\s*=\s*(["\']?)' . preg_quote($value['name']) . + '(["\'])?#'; + $rep = 'src=\1cid:' . $value['cid'] .'\2'; + $this->_htmlbody = preg_replace($regex, $rep, + $this->_htmlbody + ); + } + } + + $null = null; + $attachments = !empty($this->_parts) ? true : false; + $html_images = !empty($this->_html_images) ? true : false; + $html = !empty($this->_htmlbody) ? true : false; + $text = (!$html AND !empty($this->_txtbody)) ? true : false; + + switch (true) { + case $text AND !$attachments: + $message =& $this->_addTextPart($null, $this->_txtbody); + break; + + case !$text AND !$html AND $attachments: + $message =& $this->_addMixedPart(); + for ($i = 0; $i < count($this->_parts); $i++) { + $this->_addAttachmentPart($message, $this->_parts[$i]); + } + break; + + case $text AND $attachments: + $message =& $this->_addMixedPart(); + $this->_addTextPart($message, $this->_txtbody); + for ($i = 0; $i < count($this->_parts); $i++) { + $this->_addAttachmentPart($message, $this->_parts[$i]); + } + break; + + case $html AND !$attachments AND !$html_images: + if (isset($this->_txtbody)) { + $message =& $this->_addAlternativePart($null); + $this->_addTextPart($message, $this->_txtbody); + $this->_addHtmlPart($message); + } else { + $message =& $this->_addHtmlPart($null); + } + break; + + case $html AND !$attachments AND $html_images: + if (isset($this->_txtbody)) { + $message =& $this->_addAlternativePart($null); + $this->_addTextPart($message, $this->_txtbody); + $related =& $this->_addRelatedPart($message); + } else { + $message =& $this->_addRelatedPart($null); + $related =& $message; + } + $this->_addHtmlPart($related); + for ($i = 0; $i < count($this->_html_images); $i++) { + $this->_addHtmlImagePart($related, $this->_html_images[$i]); + } + break; + + case $html AND $attachments AND !$html_images: + $message =& $this->_addMixedPart(); + if (isset($this->_txtbody)) { + $alt =& $this->_addAlternativePart($message); + $this->_addTextPart($alt, $this->_txtbody); + $this->_addHtmlPart($alt); + } else { + $this->_addHtmlPart($message); + } + for ($i = 0; $i < count($this->_parts); $i++) { + $this->_addAttachmentPart($message, $this->_parts[$i]); + } + break; + + case $html AND $attachments AND $html_images: + $message =& $this->_addMixedPart(); + if (isset($this->_txtbody)) { + $alt =& $this->_addAlternativePart($message); + $this->_addTextPart($alt, $this->_txtbody); + $rel =& $this->_addRelatedPart($alt); + } else { + $rel =& $this->_addRelatedPart($message); + } + $this->_addHtmlPart($rel); + for ($i = 0; $i < count($this->_html_images); $i++) { + $this->_addHtmlImagePart($rel, $this->_html_images[$i]); + } + for ($i = 0; $i < count($this->_parts); $i++) { + $this->_addAttachmentPart($message, $this->_parts[$i]); + } + break; + + } + + if (isset($message)) { + $output = $message->encode(); + $this->_headers = array_merge($this->_headers, + $output['headers']); + return $output['body']; + + } else { + return false; + } + } + + /** + * Returns an array with the headers needed to prepend to the email + * (MIME-Version and Content-Type). Format of argument is: + * $array['header-name'] = 'header-value'; + * + * @param array $xtra_headers Assoc array with any extra headers. + * Optional. + * @return array Assoc array with the mime headers + * @access public + */ + function &headers($xtra_headers = null) + { + // Content-Type header should already be present, + // So just add mime version header + $headers['MIME-Version'] = '1.0'; + if (isset($xtra_headers)) { + $headers = array_merge($headers, $xtra_headers); + } + $this->_headers = array_merge($headers, $this->_headers); + + return $this->_encodeHeaders($this->_headers); + } + + /** + * Get the text version of the headers + * (usefull if you want to use the PHP mail() function) + * + * @param array $xtra_headers Assoc array with any extra headers. + * Optional. + * @return string Plain text headers + * @access public + */ + function txtHeaders($xtra_headers = null) + { + $headers = $this->headers($xtra_headers); + $ret = ''; + foreach ($headers as $key => $val) { + $ret .= "$key: $val" . MAIL_MIME_CRLF; + } + return $ret; + } + + /** + * Sets the Subject header + * + * @param string $subject String to set the subject to + * access public + */ + function setSubject($subject) + { + $this->_headers['Subject'] = $subject; + } + + /** + * Set an email to the From (the sender) header + * + * @param string $email The email direction to add + * @access public + */ + function setFrom($email) + { + $this->_headers['From'] = $email; + } + + /** + * Add an email to the Cc (carbon copy) header + * (multiple calls to this method are allowed) + * + * @param string $email The email direction to add + * @access public + */ + function addCc($email) + { + if (isset($this->_headers['Cc'])) { + $this->_headers['Cc'] .= ", $email"; + } else { + $this->_headers['Cc'] = $email; + } + } + + /** + * Add an email to the Bcc (blank carbon copy) header + * (multiple calls to this method are allowed) + * + * @param string $email The email direction to add + * @access public + */ + function addBcc($email) + { + if (isset($this->_headers['Bcc'])) { + $this->_headers['Bcc'] .= ", $email"; + } else { + $this->_headers['Bcc'] = $email; + } + } + + /** + * Encodes a header as per RFC2047 + * + * @param string $input The header data to encode + * @return string Encoded data + * @access private + */ + function _encodeHeaders($input) + { + $enc_prefix = '=?' . $this->_build_params['head_charset'] . '?Q?'; + foreach ($input as $hdr_name => $hdr_value) { + if (preg_match('/(\w*[\x80-\xFF]+\w*)/', $hdr_value)) { + $enc_value = preg_replace('/([\x80-\xFF])/e', '"=".strtoupper(dechex(ord("\1")))', $hdr_value); + // check for <email address> in string + if (preg_match('/<[a-z0-9\-\.\+\_]+@[a-z0-9]([a-z0-9\-].?)*[a-z0-9]\\.[a-z]{2,5}>/i', $enc_value) && ($p = strrpos($enc_value, '<'))) { + $hdr_value = $enc_prefix . substr($enc_value, 0, $p-1) . '?= ' . substr($enc_value, $p, strlen($enc_value)-$p); + } else { + $hdr_value = $enc_prefix . $enc_value . '?='; + } + } + $input[$hdr_name] = $hdr_value; + } + + return $input; + } + + /* replaced 2005/07/08 by roundcube@gmail.com + + function _encodeHeaders_old($input) + { + foreach ($input as $hdr_name => $hdr_value) { + preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $hdr_value, $matches); + foreach ($matches[1] as $value) { + $replacement = preg_replace('/([\x80-\xFF])/e', + '"=" . + strtoupper(dechex(ord("\1")))', + $value); + $hdr_value = str_replace($value, '=?' . + $this->_build_params['head_charset'] . + '?Q?' . $replacement . '?=', + $hdr_value); + } + $input[$hdr_name] = $hdr_value; + } + + return $input; + } + */ + + /** + * Set the object's end-of-line and define the constant if applicable + * + * @param string $eol End Of Line sequence + * @access private + */ + function _setEOL($eol) + { + $this->_eol = $eol; + if (!defined('MAIL_MIME_CRLF')) { + define('MAIL_MIME_CRLF', $this->_eol, true); + } + } + + + +} // End of class +?> diff --git a/program/lib/Mail/mimeDecode.php b/program/lib/Mail/mimeDecode.php new file mode 100644 index 000000000..92827b727 --- /dev/null +++ b/program/lib/Mail/mimeDecode.php @@ -0,0 +1,842 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2002-2003 Richard Heyes | +// | Copyright (c) 2003-2005 The PHP Group | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o Redistributions in binary form must reproduce the above copyright | +// | notice, this list of conditions and the following disclaimer in the | +// | documentation and/or other materials provided with the distribution.| +// | o The names of the authors may not be used to endorse or promote | +// | products derived from this software without specific prior written | +// | permission. | +// | | +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Richard Heyes <richard@phpguru.org> | +// +-----------------------------------------------------------------------+ + +require_once 'PEAR.php'; + +/** +* +----------------------------- IMPORTANT ------------------------------+ +* | Usage of this class compared to native php extensions such as | +* | mailparse or imap, is slow and may be feature deficient. If available| +* | you are STRONGLY recommended to use the php extensions. | +* +----------------------------------------------------------------------+ +* +* Mime Decoding class +* +* This class will parse a raw mime email and return +* the structure. Returned structure is similar to +* that returned by imap_fetchstructure(). +* +* USAGE: (assume $input is your raw email) +* +* $decode = new Mail_mimeDecode($input, "\r\n"); +* $structure = $decode->decode(); +* print_r($structure); +* +* Or statically: +* +* $params['input'] = $input; +* $structure = Mail_mimeDecode::decode($params); +* print_r($structure); +* +* TODO: +* o Implement multipart/appledouble +* o UTF8: ??? + + > 4. We have also found a solution for decoding the UTF-8 + > headers. Therefore I made the following function: + > + > function decode_utf8($txt) { + > $trans=array("Å‘"=>"õ","ű"=>"û","Å"=>"Õ","Å°" + =>"Û"); + > $txt=strtr($txt,$trans); + > return(utf8_decode($txt)); + > } + > + > And I have inserted the following line to the class: + > + > if (strtolower($charset)=="utf-8") $text=decode_utf8($text); + > + > ... before the following one in the "_decodeHeader" function: + > + > $input = str_replace($encoded, $text, $input); + > + > This way from now on it can easily decode the UTF-8 headers too. + +* +* @author Richard Heyes <richard@phpguru.org> +* @version $Revision$ +* @package Mail +*/ +class Mail_mimeDecode extends PEAR +{ + /** + * The raw email to decode + * @var string + */ + var $_input; + + /** + * The header part of the input + * @var string + */ + var $_header; + + /** + * The body part of the input + * @var string + */ + var $_body; + + /** + * If an error occurs, this is used to store the message + * @var string + */ + var $_error; + + /** + * Flag to determine whether to include bodies in the + * returned object. + * @var boolean + */ + var $_include_bodies; + + /** + * Flag to determine whether to decode bodies + * @var boolean + */ + var $_decode_bodies; + + /** + * Flag to determine whether to decode headers + * @var boolean + */ + var $_decode_headers; + + /** + * Constructor. + * + * Sets up the object, initialise the variables, and splits and + * stores the header and body of the input. + * + * @param string The input to decode + * @access public + */ + function Mail_mimeDecode($input) + { + list($header, $body) = $this->_splitBodyHeader($input); + + $this->_input = $input; + $this->_header = $header; + $this->_body = $body; + $this->_decode_bodies = false; + $this->_include_bodies = true; + } + + /** + * Begins the decoding process. If called statically + * it will create an object and call the decode() method + * of it. + * + * @param array An array of various parameters that determine + * various things: + * include_bodies - Whether to include the body in the returned + * object. + * decode_bodies - Whether to decode the bodies + * of the parts. (Transfer encoding) + * decode_headers - Whether to decode headers + * input - If called statically, this will be treated + * as the input + * @return object Decoded results + * @access public + */ + function decode($params = null) + { + // determine if this method has been called statically + $isStatic = !(isset($this) && get_class($this) == __CLASS__); + + // Have we been called statically? + // If so, create an object and pass details to that. + if ($isStatic AND isset($params['input'])) { + + $obj = new Mail_mimeDecode($params['input']); + $structure = $obj->decode($params); + + // Called statically but no input + } elseif ($isStatic) { + return PEAR::raiseError('Called statically and no input given'); + + // Called via an object + } else { + $this->_include_bodies = isset($params['include_bodies']) ? + $params['include_bodies'] : false; + $this->_decode_bodies = isset($params['decode_bodies']) ? + $params['decode_bodies'] : false; + $this->_decode_headers = isset($params['decode_headers']) ? + $params['decode_headers'] : false; + + $structure = $this->_decode($this->_header, $this->_body); + if ($structure === false) { + $structure = $this->raiseError($this->_error); + } + } + + return $structure; + } + + /** + * Performs the decoding. Decodes the body string passed to it + * If it finds certain content-types it will call itself in a + * recursive fashion + * + * @param string Header section + * @param string Body section + * @return object Results of decoding process + * @access private + */ + function _decode($headers, $body, $default_ctype = 'text/plain') + { + $return = new stdClass; + $return->headers = array(); + $headers = $this->_parseHeaders($headers); + + foreach ($headers as $value) { + if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { + $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]); + $return->headers[strtolower($value['name'])][] = $value['value']; + + } elseif (isset($return->headers[strtolower($value['name'])])) { + $return->headers[strtolower($value['name'])][] = $value['value']; + + } else { + $return->headers[strtolower($value['name'])] = $value['value']; + } + } + + reset($headers); + while (list($key, $value) = each($headers)) { + $headers[$key]['name'] = strtolower($headers[$key]['name']); + switch ($headers[$key]['name']) { + + case 'content-type': + $content_type = $this->_parseHeaderValue($headers[$key]['value']); + + if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) { + $return->ctype_primary = $regs[1]; + $return->ctype_secondary = $regs[2]; + } + + if (isset($content_type['other'])) { + while (list($p_name, $p_value) = each($content_type['other'])) { + $return->ctype_parameters[$p_name] = $p_value; + } + } + break; + + case 'content-disposition': + $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); + $return->disposition = $content_disposition['value']; + if (isset($content_disposition['other'])) { + while (list($p_name, $p_value) = each($content_disposition['other'])) { + $return->d_parameters[$p_name] = $p_value; + } + } + break; + + case 'content-transfer-encoding': + $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']); + break; + } + } + + if (isset($content_type)) { + switch (strtolower($content_type['value'])) { + case 'text/plain': + $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; + $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null; + break; + + case 'text/html': + $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; + $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null; + break; + + case 'multipart/parallel': + case 'multipart/report': // RFC1892 + case 'multipart/signed': // PGP + case 'multipart/digest': + case 'multipart/alternative': + case 'multipart/related': + case 'multipart/mixed': + if(!isset($content_type['other']['boundary'])){ + $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; + return false; + } + + $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain'; + $parts = $this->_boundarySplit($body, $content_type['other']['boundary']); + for ($i = 0; $i < count($parts); $i++) { + list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]); + $part = $this->_decode($part_header, $part_body, $default_ctype); + if($part === false) + $part = $this->raiseError($this->_error); + $return->parts[] = $part; + } + break; + + case 'message/rfc822': + $obj = &new Mail_mimeDecode($body); + $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, + 'decode_bodies' => $this->_decode_bodies, + 'decode_headers' => $this->_decode_headers)); + unset($obj); + break; + + default: + if(!isset($content_transfer_encoding['value'])) + $content_transfer_encoding['value'] = '7bit'; + $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null; + break; + } + + } else { + $ctype = explode('/', $default_ctype); + $return->ctype_primary = $ctype[0]; + $return->ctype_secondary = $ctype[1]; + $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; + } + + return $return; + } + + /** + * Given the output of the above function, this will return an + * array of references to the parts, indexed by mime number. + * + * @param object $structure The structure to go through + * @param string $mime_number Internal use only. + * @return array Mime numbers + */ + function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') + { + $return = array(); + if (!empty($structure->parts)) { + if ($mime_number != '') { + $structure->mime_id = $prepend . $mime_number; + $return[$prepend . $mime_number] = &$structure; + } + for ($i = 0; $i < count($structure->parts); $i++) { + + + if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { + $prepend = $prepend . $mime_number . '.'; + $_mime_number = ''; + } else { + $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); + } + + $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); + foreach ($arr as $key => $val) { + $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; + } + } + } else { + if ($mime_number == '') { + $mime_number = '1'; + } + $structure->mime_id = $prepend . $mime_number; + $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; + } + + return $return; + } + + /** + * Given a string containing a header and body + * section, this function will split them (at the first + * blank line) and return them. + * + * @param string Input to split apart + * @return array Contains header and body section + * @access private + */ + function _splitBodyHeader($input) + { + if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { + return array($match[1], $match[2]); + } + $this->_error = 'Could not split header and body'; + return false; + } + + /** + * Parse headers given in $input and return + * as assoc array. + * + * @param string Headers to parse + * @return array Contains parsed headers + * @access private + */ + function _parseHeaders($input) + { + + if ($input !== '') { + // Unfold the input + $input = preg_replace("/\r?\n/", "\r\n", $input); + $input = preg_replace("/\r\n(\t| )+/", ' ', $input); + $headers = explode("\r\n", trim($input)); + + foreach ($headers as $value) { + $hdr_name = substr($value, 0, $pos = strpos($value, ':')); + $hdr_value = substr($value, $pos+1); + if($hdr_value[0] == ' ') + $hdr_value = substr($hdr_value, 1); + + $return[] = array( + 'name' => $hdr_name, + 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value + ); + } + } else { + $return = array(); + } + + return $return; + } + + /** + * Function to parse a header value, + * extract first part, and any secondary + * parts (after ;) This function is not as + * robust as it could be. Eg. header comments + * in the wrong place will probably break it. + * + * @param string Header value to parse + * @return array Contains parsed result + * @access private + */ + function _parseHeaderValue($input) + { + + if (($pos = strpos($input, ';')) !== false) { + + $return['value'] = trim(substr($input, 0, $pos)); + $input = trim(substr($input, $pos+1)); + + if (strlen($input) > 0) { + + // This splits on a semi-colon, if there's no preceeding backslash + // Now works with quoted values; had to glue the \; breaks in PHP + // the regex is already bordering on incomprehensible + $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; + preg_match_all($splitRegex, $input, $matches); + $parameters = array(); + for ($i=0; $i<count($matches[0]); $i++) { + $param = $matches[0][$i]; + while (substr($param, -2) == '\;') { + $param .= $matches[0][++$i]; + } + $parameters[] = $param; + } + + for ($i = 0; $i < count($parameters); $i++) { + $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); + $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); + if ($param_value[0] == '"') { + $param_value = substr($param_value, 1, -1); + } + $return['other'][$param_name] = $param_value; + $return['other'][strtolower($param_name)] = $param_value; + } + } + } else { + $return['value'] = trim($input); + } + + return $return; + } + + /** + * This function splits the input based + * on the given boundary + * + * @param string Input to parse + * @return array Contains array of resulting mime parts + * @access private + */ + function _boundarySplit($input, $boundary) + { + $parts = array(); + + $bs_possible = substr($boundary, 2, -2); + $bs_check = '\"' . $bs_possible . '\"'; + + if ($boundary == $bs_check) { + $boundary = $bs_possible; + } + + $tmp = explode('--' . $boundary, $input); + $count = count($tmp); + + // when boundaries are set correctly we should have at least 3 parts; + // if not, return the last one (tbr) + if ($count<3) + return array($tmp[$count-1]); + + for ($i = 1; $i < $count - 1; $i++) { + $parts[] = $tmp[$i]; + } + + return $parts; + } + + /** + * Given a header, this function will decode it + * according to RFC2047. Probably not *exactly* + * conformant, but it does pass all the given + * examples (in RFC2047). + * + * @param string Input header value to decode + * @return string Decoded header value + * @access private + */ + function _decodeHeader($input) + { + // Remove white space between encoded-words + $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); + + // For each encoded-word... + while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { + + $encoded = $matches[1]; + $charset = $matches[2]; + $encoding = $matches[3]; + $text = $matches[4]; + + switch (strtolower($encoding)) { + case 'b': + $text = base64_decode($text); + break; + + case 'q': + $text = str_replace('_', ' ', $text); + preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); + foreach($matches[1] as $value) + $text = str_replace('='.$value, chr(hexdec($value)), $text); + break; + } + + $input = str_replace($encoded, $text, $input); + } + + return $input; + } + + /** + * Given a body string and an encoding type, + * this function will decode and return it. + * + * @param string Input body to decode + * @param string Encoding type to use. + * @return string Decoded body + * @access private + */ + function _decodeBody($input, $encoding = '7bit') + { + switch (strtolower($encoding)) { + case '7bit': + return $input; + break; + + case 'quoted-printable': + return $this->_quotedPrintableDecode($input); + break; + + case 'base64': + return base64_decode($input); + break; + + default: + return $input; + } + } + + /** + * Given a quoted-printable string, this + * function will decode and return it. + * + * @param string Input body to decode + * @return string Decoded body + * @access private + */ + function _quotedPrintableDecode($input) + { + // Remove soft line breaks + $input = preg_replace("/=\r?\n/", '', $input); + + // Replace encoded characters + $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input); + + return $input; + } + + /** + * Checks the input for uuencoded files and returns + * an array of them. Can be called statically, eg: + * + * $files =& Mail_mimeDecode::uudecode($some_text); + * + * It will check for the begin 666 ... end syntax + * however and won't just blindly decode whatever you + * pass it. + * + * @param string Input body to look for attahcments in + * @return array Decoded bodies, filenames and permissions + * @access public + * @author Unknown + */ + function &uudecode($input) + { + // Find all uuencoded sections + preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); + + for ($j = 0; $j < count($matches[3]); $j++) { + + $str = $matches[3][$j]; + $filename = $matches[2][$j]; + $fileperm = $matches[1][$j]; + + $file = ''; + $str = preg_split("/\r?\n/", trim($str)); + $strlen = count($str); + + for ($i = 0; $i < $strlen; $i++) { + $pos = 1; + $d = 0; + $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); + + while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { + $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); + $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); + $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); + $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); + $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); + + $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); + + $file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077)); + + $pos += 4; + $d += 3; + } + + if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { + $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); + $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); + $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); + $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); + + $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); + + $pos += 3; + $d += 2; + } + + if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { + $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); + $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); + $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); + + } + } + $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); + } + + return $files; + } + + /** + * getSendArray() returns the arguments required for Mail::send() + * used to build the arguments for a mail::send() call + * + * Usage: + * $mailtext = Full email (for example generated by a template) + * $decoder = new Mail_mimeDecode($mailtext); + * $parts = $decoder->getSendArray(); + * if (!PEAR::isError($parts) { + * list($recipents,$headers,$body) = $parts; + * $mail = Mail::factory('smtp'); + * $mail->send($recipents,$headers,$body); + * } else { + * echo $parts->message; + * } + * @return mixed array of recipeint, headers,body or Pear_Error + * @access public + * @author Alan Knowles <alan@akbkhome.com> + */ + function getSendArray() + { + // prevent warning if this is not set + $this->_decode_headers = FALSE; + $headerlist =$this->_parseHeaders($this->_header); + $to = ""; + if (!$headerlist) { + return $this->raiseError("Message did not contain headers"); + } + foreach($headerlist as $item) { + $header[$item['name']] = $item['value']; + switch (strtolower($item['name'])) { + case "to": + case "cc": + case "bcc": + $to = ",".$item['value']; + default: + break; + } + } + if ($to == "") { + return $this->raiseError("Message did not contain any recipents"); + } + $to = substr($to,1); + return array($to,$header,$this->_body); + } + + /** + * Returns a xml copy of the output of + * Mail_mimeDecode::decode. Pass the output in as the + * argument. This function can be called statically. Eg: + * + * $output = $obj->decode(); + * $xml = Mail_mimeDecode::getXML($output); + * + * The DTD used for this should have been in the package. Or + * alternatively you can get it from cvs, or here: + * http://www.phpguru.org/xmail/xmail.dtd. + * + * @param object Input to convert to xml. This should be the + * output of the Mail_mimeDecode::decode function + * @return string XML version of input + * @access public + */ + function getXML($input) + { + $crlf = "\r\n"; + $output = '<?xml version=\'1.0\'?>' . $crlf . + '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf . + '<email>' . $crlf . + Mail_mimeDecode::_getXML($input) . + '</email>'; + + return $output; + } + + /** + * Function that does the actual conversion to xml. Does a single + * mimepart at a time. + * + * @param object Input to convert to xml. This is a mimepart object. + * It may or may not contain subparts. + * @param integer Number of tabs to indent + * @return string XML version of input + * @access private + */ + function _getXML($input, $indent = 1) + { + $htab = "\t"; + $crlf = "\r\n"; + $output = ''; + $headers = @(array)$input->headers; + + foreach ($headers as $hdr_name => $hdr_value) { + + // Multiple headers with this name + if (is_array($headers[$hdr_name])) { + for ($i = 0; $i < count($hdr_value); $i++) { + $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); + } + + // Only one header of this sort + } else { + $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); + } + } + + if (!empty($input->parts)) { + for ($i = 0; $i < count($input->parts); $i++) { + $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf . + Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . + str_repeat($htab, $indent) . '</mimepart>' . $crlf; + } + } elseif (isset($input->body)) { + $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' . + $input->body . ']]></body>' . $crlf; + } + + return $output; + } + + /** + * Helper function to _getXML(). Returns xml of a header. + * + * @param string Name of header + * @param string Value of header + * @param integer Number of tabs to indent + * @return string XML version of input + * @access private + */ + function _getXML_helper($hdr_name, $hdr_value, $indent) + { + $htab = "\t"; + $crlf = "\r\n"; + $return = ''; + + $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); + $new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); + + // Sort out any parameters + if (!empty($new_hdr_value['other'])) { + foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { + $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf . + str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf . + str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf . + str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf; + } + + $params = implode('', $params); + } else { + $params = ''; + } + + $return = str_repeat($htab, $indent) . '<header>' . $crlf . + str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf . + str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf . + $params . + str_repeat($htab, $indent) . '</header>' . $crlf; + + return $return; + } + +} // End of class +?> diff --git a/program/lib/Mail/mimePart.php b/program/lib/Mail/mimePart.php new file mode 100644 index 000000000..b429b905e --- /dev/null +++ b/program/lib/Mail/mimePart.php @@ -0,0 +1,351 @@ +<?php +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2002-2003 Richard Heyes | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o Redistributions in binary form must reproduce the above copyright | +// | notice, this list of conditions and the following disclaimer in the | +// | documentation and/or other materials provided with the distribution.| +// | o The names of the authors may not be used to endorse or promote | +// | products derived from this software without specific prior written | +// | permission. | +// | | +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Richard Heyes <richard@phpguru.org> | +// +-----------------------------------------------------------------------+ + +/** +* +* Raw mime encoding class +* +* What is it? +* This class enables you to manipulate and build +* a mime email from the ground up. +* +* Why use this instead of mime.php? +* mime.php is a userfriendly api to this class for +* people who aren't interested in the internals of +* mime mail. This class however allows full control +* over the email. +* +* Eg. +* +* // Since multipart/mixed has no real body, (the body is +* // the subpart), we set the body argument to blank. +* +* $params['content_type'] = 'multipart/mixed'; +* $email = new Mail_mimePart('', $params); +* +* // Here we add a text part to the multipart we have +* // already. Assume $body contains plain text. +* +* $params['content_type'] = 'text/plain'; +* $params['encoding'] = '7bit'; +* $text = $email->addSubPart($body, $params); +* +* // Now add an attachment. Assume $attach is +* the contents of the attachment +* +* $params['content_type'] = 'application/zip'; +* $params['encoding'] = 'base64'; +* $params['disposition'] = 'attachment'; +* $params['dfilename'] = 'example.zip'; +* $attach =& $email->addSubPart($body, $params); +* +* // Now build the email. Note that the encode +* // function returns an associative array containing two +* // elements, body and headers. You will need to add extra +* // headers, (eg. Mime-Version) before sending. +* +* $email = $message->encode(); +* $email['headers'][] = 'Mime-Version: 1.0'; +* +* +* Further examples are available at http://www.phpguru.org +* +* TODO: +* - Set encode() to return the $obj->encoded if encode() +* has already been run. Unless a flag is passed to specifically +* re-build the message. +* +* @author Richard Heyes <richard@phpguru.org> +* @version $Revision$ +* @package Mail +*/ + +class Mail_mimePart { + + /** + * The encoding type of this part + * @var string + */ + var $_encoding; + + /** + * An array of subparts + * @var array + */ + var $_subparts; + + /** + * The output of this part after being built + * @var string + */ + var $_encoded; + + /** + * Headers for this part + * @var array + */ + var $_headers; + + /** + * The body of this part (not encoded) + * @var string + */ + var $_body; + + /** + * Constructor. + * + * Sets up the object. + * + * @param $body - The body of the mime part if any. + * @param $params - An associative array of parameters: + * content_type - The content type for this part eg multipart/mixed + * encoding - The encoding to use, 7bit, 8bit, base64, or quoted-printable + * cid - Content ID to apply + * disposition - Content disposition, inline or attachment + * dfilename - Optional filename parameter for content disposition + * description - Content description + * charset - Character set to use + * @access public + */ + function Mail_mimePart($body = '', $params = array()) + { + if (!defined('MAIL_MIMEPART_CRLF')) { + define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE); + } + + foreach ($params as $key => $value) { + switch ($key) { + case 'content_type': + $headers['Content-Type'] = $value . (isset($charset) ? '; charset="' . $charset . '"' : ''); + break; + + case 'encoding': + $this->_encoding = $value; + $headers['Content-Transfer-Encoding'] = $value; + break; + + case 'cid': + $headers['Content-ID'] = '<' . $value . '>'; + break; + + case 'disposition': + $headers['Content-Disposition'] = $value . (isset($dfilename) ? '; filename="' . $dfilename . '"' : ''); + break; + + case 'dfilename': + if (isset($headers['Content-Disposition'])) { + $headers['Content-Disposition'] .= '; filename="' . $value . '"'; + } else { + $dfilename = $value; + } + break; + + case 'description': + $headers['Content-Description'] = $value; + break; + + case 'charset': + if (isset($headers['Content-Type'])) { + $headers['Content-Type'] .= '; charset="' . $value . '"'; + } else { + $charset = $value; + } + break; + } + } + + // Default content-type + if (!isset($headers['Content-Type'])) { + $headers['Content-Type'] = 'text/plain'; + } + + //Default encoding + if (!isset($this->_encoding)) { + $this->_encoding = '7bit'; + } + + // Assign stuff to member variables + $this->_encoded = array(); + $this->_headers = $headers; + $this->_body = $body; + } + + /** + * encode() + * + * Encodes and returns the email. Also stores + * it in the encoded member variable + * + * @return An associative array containing two elements, + * body and headers. The headers element is itself + * an indexed array. + * @access public + */ + function encode() + { + $encoded =& $this->_encoded; + + if (!empty($this->_subparts)) { + srand((double)microtime()*1000000); + $boundary = '=_' . md5(rand() . microtime()); + $this->_headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF . "\t" . 'boundary="' . $boundary . '"'; + + // Add body parts to $subparts + for ($i = 0; $i < count($this->_subparts); $i++) { + $headers = array(); + $tmp = $this->_subparts[$i]->encode(); + foreach ($tmp['headers'] as $key => $value) { + $headers[] = $key . ': ' . $value; + } + $subparts[] = implode(MAIL_MIMEPART_CRLF, $headers) . MAIL_MIMEPART_CRLF . MAIL_MIMEPART_CRLF . $tmp['body']; + } + + $encoded['body'] = '--' . $boundary . MAIL_MIMEPART_CRLF . + implode('--' . $boundary . MAIL_MIMEPART_CRLF, $subparts) . + '--' . $boundary.'--' . MAIL_MIMEPART_CRLF; + + } else { + $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding) . MAIL_MIMEPART_CRLF; + } + + // Add headers to $encoded + $encoded['headers'] =& $this->_headers; + + return $encoded; + } + + /** + * &addSubPart() + * + * Adds a subpart to current mime part and returns + * a reference to it + * + * @param $body The body of the subpart, if any. + * @param $params The parameters for the subpart, same + * as the $params argument for constructor. + * @return A reference to the part you just added. It is + * crucial if using multipart/* in your subparts that + * you use =& in your script when calling this function, + * otherwise you will not be able to add further subparts. + * @access public + */ + function &addSubPart($body, $params) + { + $this->_subparts[] = new Mail_mimePart($body, $params); + return $this->_subparts[count($this->_subparts) - 1]; + } + + /** + * _getEncodedData() + * + * Returns encoded data based upon encoding passed to it + * + * @param $data The data to encode. + * @param $encoding The encoding type to use, 7bit, base64, + * or quoted-printable. + * @access private + */ + function _getEncodedData($data, $encoding) + { + switch ($encoding) { + case '8bit': + case '7bit': + return $data; + break; + + case 'quoted-printable': + return $this->_quotedPrintableEncode($data); + break; + + case 'base64': + return rtrim(chunk_split(base64_encode($data), 76, MAIL_MIMEPART_CRLF)); + break; + + default: + return $data; + } + } + + /** + * quoteadPrintableEncode() + * + * Encodes data to quoted-printable standard. + * + * @param $input The data to encode + * @param $line_max Optional max line length. Should + * not be more than 76 chars + * + * @access private + */ + function _quotedPrintableEncode($input , $line_max = 76) + { + $lines = preg_split("/\r?\n/", $input); + $eol = MAIL_MIMEPART_CRLF; + $escape = '='; + $output = ''; + + while(list(, $line) = each($lines)){ + + $linlen = strlen($line); + $newline = ''; + + for ($i = 0; $i < $linlen; $i++) { + $char = substr($line, $i, 1); + $dec = ord($char); + + if (($dec == 32) AND ($i == ($linlen - 1))){ // convert space at eol only + $char = '=20'; + + } elseif(($dec == 9) AND ($i == ($linlen - 1))) { // convert tab at eol only + $char = '=09'; + } elseif($dec == 9) { + ; // Do nothing if a tab. + } elseif(($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) { + $char = $escape . strtoupper(sprintf('%02s', dechex($dec))); + } + + if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted + $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay + $newline = ''; + } + $newline .= $char; + } // end of for + $output .= $newline . $eol; + } + $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf + return $output; + } +} // End of class +?> diff --git a/program/lib/PEAR.php b/program/lib/PEAR.php new file mode 100644 index 000000000..5b76d7540 --- /dev/null +++ b/program/lib/PEAR.php @@ -0,0 +1,927 @@ +<?php +// +// +----------------------------------------------------------------------+ +// | PHP Version 4 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2002 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 2.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available at through the world-wide-web at | +// | http://www.php.net/license/2_02.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Authors: Sterling Hughes <sterling@php.net> | +// | Stig Bakken <ssb@fast.no> | +// | Tomas V.V.Cox <cox@idecnet.com> | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +define('PEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference, ej: $obj =& new PEAR_child; + * + * @since PHP 4.0.2 + * @author Stig Bakken <ssb@fast.no> + * @see http://pear.php.net/manual/ + */ +class PEAR +{ + // {{{ properties + + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + // }}} + + // {{{ constructor + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @access public + * @return void + */ + function PEAR($error_class = null) + { + $classname = get_class($this); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + if ($error_class !== null) { + $this->_error_class = $error_class; + } + while ($classname) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = &$this; + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + // }}} + // {{{ destructor + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @access public + * @return void + */ + function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", get_class($this)); + } + } + + // }}} + // {{{ getStaticProperty() + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + function &getStaticProperty($class, $var) + { + static $properties; + return $properties[$class][$var]; + } + + // }}} + // {{{ registerShutdownFunc() + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + function registerShutdownFunc($func, $args = array()) + { + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + // }}} + // {{{ isError() + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @access public + * @return bool true if parameter is an error + */ + function isError($data) { + return (bool)(is_object($data) && + (get_class($data) == 'pear_error' || + is_subclass_of($data, 'pear_error'))); + } + + // }}} + // {{{ setErrorHandling() + + /** + * Sets how errors generated by this DB object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE or + * PEAR_ERROR_CALLBACK. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * + * @since PHP 4.0.5 + */ + + function setErrorHandling($mode = null, $options = null) + { + if (isset($this)) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + if ((is_string($options) && function_exists($options)) || + (is_array($options) && method_exists(@$options[0], @$options[1]))) + { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + // }}} + // {{{ expectError() + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return sizeof($this->_expected_errors); + } + + // }}} + // {{{ popExpect() + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + // }}} + // {{{ _checkDelExpect() + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + + foreach ($this->_expected_errors AS $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + return $deleted; + } + + // }}} + // {{{ delExpect() + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; + // we walk through it trying to unset all + // values + foreach($error_code AS $key => $error) { + if ($this->_checkDelExpect($error)) { + $deleted = true; + } else { + $deleted = false; + } + } + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } else { + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + } else { + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + } + + // }}} + // {{{ raiseError() + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE or + * PEAR_ERROR_CALLBACK. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @access public + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + function &raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message = $message->getMessage(); + } + + if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp))) { + $mode = PEAR_ERROR_RETURN; + } + } + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'PEAR_Error'; + } + if ($skipmsg) { + return new $ec($code, $mode, $options, $userinfo); + } else { + return new $ec($message, $code, $mode, $options, $userinfo); + } + } + + // }}} + // {{{ throwError() + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param string $message + * + */ + function &throwError($message = null, + $code = null, + $userinfo = null) + { + if (isset($this)) { + return $this->raiseError($message, $code, null, null, $userinfo); + } else { + return PEAR::raiseError($message, $code, null, null, $userinfo); + } + } + + // }}} + // {{{ pushErrorHandling() + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if (isset($this)) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this)) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + // }}} + // {{{ popErrorHandling() + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + function popErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this)) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + // }}} + // {{{ loadExtension() + + /** + * OS independant PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + function loadExtension($ext) + { + if (!extension_loaded($ext)) { + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } + return true; + } + + // }}} +} + +// {{{ _PEAR_call_destructors() + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +// }}} + +class PEAR_Error +{ + // {{{ properties + + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + + // Wait until we have a stack-groping function in PHP. + //var $file = ''; + //var $line = 0; + + + // }}} + // {{{ constructor + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER or + * PEAR_ERROR_CALLBACK + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function PEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + $this->level = $options; + $this->callback = null; + } + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + printf($format, $this->getMessage()); + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_string($this->callback) && strlen($this->callback)) { + call_user_func($this->callback, $this); + } elseif (is_array($this->callback) && + sizeof($this->callback) == 2 && + is_object($this->callback[0]) && + is_string($this->callback[1]) && + strlen($this->callback[1])) { + @call_user_func($this->callback, $this); + } + } + } + + // }}} + // {{{ getMode() + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() { + return $this->mode; + } + + // }}} + // {{{ getCallback() + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() { + return $this->callback; + } + + // }}} + // {{{ getMessage() + + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + + // }}} + // {{{ getCode() + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + // }}} + // {{{ getType() + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + // }}} + // {{{ getUserInfo() + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + // }}} + // {{{ getDebugInfo() + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + // }}} + // {{{ addUserInfo() + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + // }}} + // {{{ toString() + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = get_class($this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + get_class($this), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + get_class($this), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } + + // }}} +} + +register_shutdown_function("_PEAR_call_destructors"); + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?> diff --git a/program/lib/des.inc b/program/lib/des.inc new file mode 100644 index 000000000..00ecd688f --- /dev/null +++ b/program/lib/des.inc @@ -0,0 +1,218 @@ +<?php + +//PHP version +//Paul Tero, July 2001 +//http://www.shopable.co.uk/des.html +// +//Optimised for performance with large blocks by Michael Hayworth, November 2001 +//http://www.netdealing.com +// +//Converted from JavaScript to PHP by Jim Gibbs, June 2004 +// +//THIS SOFTWARE IS PROVIDED "AS IS" AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. + +//des +//this takes the key, the message, and whether to encrypt or decrypt +function des ($key, $message, $encrypt, $mode, $iv) { + //declaring this locally speeds things up a bit + $spfunction1 = array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004); + $spfunction2 = array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000); + $spfunction3 = array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200); + $spfunction4 = array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080); + $spfunction5 = array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100); + $spfunction6 = array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010); + $spfunction7 = array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002); + $spfunction8 = array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000); + $masks = array (4294967295,2147483647,1073741823,536870911,268435455,134217727,67108863,33554431,16777215,8388607,4194303,2097151,1048575,524287,262143,131071,65535,32767,16383,8191,4095,2047,1023,511,255,127,63,31,15,7,3,1,0); + + //create the 16 or 48 subkeys we will need + $keys = des_createKeys ($key); + $m=0; + $len = strlen($message); + $chunk = 0; + //set up the loops for single and triple des + $iterations = ((count($keys) == 32) ? 3 : 9); //single or triple des + if ($iterations == 3) {$looping = (($encrypt) ? array (0, 32, 2) : array (30, -2, -2));} + else {$looping = (($encrypt) ? array (0, 32, 2, 62, 30, -2, 64, 96, 2) : array (94, 62, -2, 32, 64, 2, 30, -2, -2));} + + $message .= (chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0)); //pad the message out with null bytes + //store the result here + $result = ""; + $tempresult = ""; + + if ($mode == 1) { //CBC mode + $cbcleft = (ord($iv{$m++}) << 24) | (ord($iv{$m++}) << 16) | (ord($iv{$m++}) << 8) | ord($iv{$m++}); + $cbcright = (ord($iv{$m++}) << 24) | (ord($iv{$m++}) << 16) | (ord($iv{$m++}) << 8) | ord($iv{$m++}); + $m=0; + } + + //loop through each 64 bit chunk of the message + while ($m < $len) { + $left = (ord($message{$m++}) << 24) | (ord($message{$m++}) << 16) | (ord($message{$m++}) << 8) | ord($message{$m++}); + $right = (ord($message{$m++}) << 24) | (ord($message{$m++}) << 16) | (ord($message{$m++}) << 8) | ord($message{$m++}); + + //for Cipher Block Chaining mode, xor the message with the previous result + if ($mode == 1) {if ($encrypt) {$left ^= $cbcleft; $right ^= $cbcright;} else {$cbcleft2 = $cbcleft; $cbcright2 = $cbcright; $cbcleft = $left; $cbcright = $right;}} + + //first each 64 but chunk of the message must be permuted according to IP + $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f; $right ^= $temp; $left ^= ($temp << 4); + $temp = (($left >> 16 & $masks[16]) ^ $right) & 0x0000ffff; $right ^= $temp; $left ^= ($temp << 16); + $temp = (($right >> 2 & $masks[2]) ^ $left) & 0x33333333; $left ^= $temp; $right ^= ($temp << 2); + $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff; $left ^= $temp; $right ^= ($temp << 8); + $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); + + $left = (($left << 1) | ($left >> 31 & $masks[31])); + $right = (($right << 1) | ($right >> 31 & $masks[31])); + + //do this either 1 or 3 times for each chunk of the message + for ($j=0; $j<$iterations; $j+=3) { + $endloop = $looping[$j+1]; + $loopinc = $looping[$j+2]; + //now go through and perform the encryption or decryption + for ($i=$looping[$j]; $i!=$endloop; $i+=$loopinc) { //for efficiency + $right1 = $right ^ $keys[$i]; + $right2 = (($right >> 4 & $masks[4]) | ($right << 28)) ^ $keys[$i+1]; + //the result is attained by passing these bytes through the S selection functions + $temp = $left; + $left = $right; + $right = $temp ^ ($spfunction2[($right1 >> 24 & $masks[24]) & 0x3f] | $spfunction4[($right1 >> 16 & $masks[16]) & 0x3f] + | $spfunction6[($right1 >> 8 & $masks[8]) & 0x3f] | $spfunction8[$right1 & 0x3f] + | $spfunction1[($right2 >> 24 & $masks[24]) & 0x3f] | $spfunction3[($right2 >> 16 & $masks[16]) & 0x3f] + | $spfunction5[($right2 >> 8 & $masks[8]) & 0x3f] | $spfunction7[$right2 & 0x3f]); + } + $temp = $left; $left = $right; $right = $temp; //unreverse left and right + } //for either 1 or 3 iterations + + //move then each one bit to the right + $left = (($left >> 1 & $masks[1]) | ($left << 31)); + $right = (($right >> 1 & $masks[1]) | ($right << 31)); + + //now perform IP-1, which is IP in the opposite direction + $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); + $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff; $left ^= $temp; $right ^= ($temp << 8); + $temp = (($right >> 2 & $masks[2]) ^ $left) & 0x33333333; $left ^= $temp; $right ^= ($temp << 2); + $temp = (($left >> 16 & $masks[16]) ^ $right) & 0x0000ffff; $right ^= $temp; $left ^= ($temp << 16); + $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f; $right ^= $temp; $left ^= ($temp << 4); + + //for Cipher Block Chaining mode, xor the message with the previous result + if ($mode == 1) {if ($encrypt) {$cbcleft = $left; $cbcright = $right;} else {$left ^= $cbcleft2; $right ^= $cbcright2;}} + $tempresult .= (chr($left>>24 & $masks[24]) . chr(($left>>16 & $masks[16]) & 0xff) . chr(($left>>8 & $masks[8]) & 0xff) . chr($left & 0xff) . chr($right>>24 & $masks[24]) . chr(($right>>16 & $masks[16]) & 0xff) . chr(($right>>8 & $masks[8]) & 0xff) . chr($right & 0xff)); + + $chunk += 8; + if ($chunk == 512) {$result .= $tempresult; $tempresult = ""; $chunk = 0;} + } //for every 8 characters, or 64 bits in the message + + //return the result as an array + return ($result . $tempresult); +} //end of des + +//des_createKeys +//this takes as input a 64 bit key (even though only 56 bits are used) +//as an array of 2 integers, and returns 16 48 bit keys +function des_createKeys ($key) { + //declaring this locally speeds things up a bit + $pc2bytes0 = array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204); + $pc2bytes1 = array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101); + $pc2bytes2 = array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808); + $pc2bytes3 = array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000); + $pc2bytes4 = array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010); + $pc2bytes5 = array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420); + $pc2bytes6 = array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002); + $pc2bytes7 = array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800); + $pc2bytes8 = array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002); + $pc2bytes9 = array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408); + $pc2bytes10 = array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020); + $pc2bytes11 = array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200); + $pc2bytes12 = array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010); + $pc2bytes13 = array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105); + $masks = array (4294967295,2147483647,1073741823,536870911,268435455,134217727,67108863,33554431,16777215,8388607,4194303,2097151,1048575,524287,262143,131071,65535,32767,16383,8191,4095,2047,1023,511,255,127,63,31,15,7,3,1,0); + + //how many iterations (1 for des, 3 for triple des) + $iterations = ((strlen($key) >= 24) ? 3 : 1); + //stores the return keys + $keys = array (); // size = 32 * iterations but you don't specify this in php + //now define the left shifts which need to be done + $shifts = array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0); + //other variables + $m=0; + $n=0; + + for ($j=0; $j<$iterations; $j++) { //either 1 or 3 iterations + $left = (ord($key{$m++}) << 24) | (ord($key{$m++}) << 16) | (ord($key{$m++}) << 8) | ord($key{$m++}); + $right = (ord($key{$m++}) << 24) | (ord($key{$m++}) << 16) | (ord($key{$m++}) << 8) | ord($key{$m++}); + + $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f; $right ^= $temp; $left ^= ($temp << 4); + $temp = (($right >> 16 & $masks[16]) ^ $left) & 0x0000ffff; $left ^= $temp; $right ^= ($temp << -16); + $temp = (($left >> 2 & $masks[2]) ^ $right) & 0x33333333; $right ^= $temp; $left ^= ($temp << 2); + $temp = (($right >> 16 & $masks[16]) ^ $left) & 0x0000ffff; $left ^= $temp; $right ^= ($temp << -16); + $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); + $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff; $left ^= $temp; $right ^= ($temp << 8); + $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555; $right ^= $temp; $left ^= ($temp << 1); + + //the right side needs to be shifted and to get the last four bits of the left side + $temp = ($left << 8) | (($right >> 20 & $masks[20]) & 0x000000f0); + //left needs to be put upside down + $left = ($right << 24) | (($right << 8) & 0xff0000) | (($right >> 8 & $masks[8]) & 0xff00) | (($right >> 24 & $masks[24]) & 0xf0); + $right = $temp; + + //now go through and perform these shifts on the left and right keys + for ($i=0; $i < count($shifts); $i++) { + //shift the keys either one or two bits to the left + if ($shifts[$i] > 0) { + $left = (($left << 2) | ($left >> 26 & $masks[26])); + $right = (($right << 2) | ($right >> 26 & $masks[26])); + } else { + $left = (($left << 1) | ($left >> 27 & $masks[27])); + $right = (($right << 1) | ($right >> 27 & $masks[27])); + } + $left = $left & -0xf; + $right = $right & -0xf; + + //now apply PC-2, in such a way that E is easier when encrypting or decrypting + //this conversion will look like PC-2 except only the last 6 bits of each byte are used + //rather than 48 consecutive bits and the order of lines will be according to + //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7 + $lefttemp = $pc2bytes0[$left >> 28 & $masks[28]] | $pc2bytes1[($left >> 24 & $masks[24]) & 0xf] + | $pc2bytes2[($left >> 20 & $masks[20]) & 0xf] | $pc2bytes3[($left >> 16 & $masks[16]) & 0xf] + | $pc2bytes4[($left >> 12 & $masks[12]) & 0xf] | $pc2bytes5[($left >> 8 & $masks[8]) & 0xf] + | $pc2bytes6[($left >> 4 & $masks[4]) & 0xf]; + $righttemp = $pc2bytes7[$right >> 28 & $masks[28]] | $pc2bytes8[($right >> 24 & $masks[24]) & 0xf] + | $pc2bytes9[($right >> 20 & $masks[20]) & 0xf] | $pc2bytes10[($right >> 16 & $masks[16]) & 0xf] + | $pc2bytes11[($right >> 12 & $masks[12]) & 0xf] | $pc2bytes12[($right >> 8 & $masks[8]) & 0xf] + | $pc2bytes13[($right >> 4 & $masks[4]) & 0xf]; + $temp = (($righttemp >> 16 & $masks[16]) ^ $lefttemp) & 0x0000ffff; + $keys[$n++] = $lefttemp ^ $temp; $keys[$n++] = $righttemp ^ ($temp << 16); + } + } //for each iterations + //return the keys we've created + return $keys; +} //end of des_createKeys + +/* +////////////////////////////// TEST ////////////////////////////// +function stringToHex ($s) { + $r = "0x"; + $hexes = array ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"); + for ($i=0; $i<strlen($s); $i++) {$r .= ($hexes [(ord($s{$i}) >> 4)] . $hexes [(ord($s{$i}) & 0xf)]);} + return $r; +} +echo "<PRE>"; +$key = "this is a 24 byte key !!"; +$message = "This is a test message"; +$ciphertext = des ($key, $message, 1, 0, null); +echo "DES Test Encrypted: " . stringToHex ($ciphertext); +$recovered_message = des ($key, $ciphertext, 0, 0, null); +echo "\n"; +echo "DES Test Decrypted: " . $recovered_message; +*/ +?>
\ No newline at end of file diff --git a/program/lib/enriched.inc b/program/lib/enriched.inc new file mode 100644 index 000000000..2435a8233 --- /dev/null +++ b/program/lib/enriched.inc @@ -0,0 +1,114 @@ +<?php +/* + File: read_enriched.inc + Author: Ryo Chijiiwa + License: GPL (part of IlohaMail) + Purpose: functions for handling text/enriched messages + Reference: RFC 1523, 1896 +*/ + + +function enriched_convert_newlines($str){ + //remove single newlines, convert N newlines to N-1 + + $str = str_replace("\r\n","\n",$str); + $len = strlen($str); + + $nl = 0; + $out = ''; + for($i=0;$i<$len;$i++){ + $c = $str[$i]; + if (ord($c)==10) $nl++; + if ($nl && ord($c)!=10) $nl = 0; + if ($nl!=1) $out.=$c; + else $out.=' '; + } + return $out; +} + +function enriched_convert_formatting($body){ + $a=array('<bold>'=>'<b>','</bold>'=>'</b>','<italic>'=>'<i>', + '</italic>'=>'</i>','<fixed>'=>'<tt>','</fixed>'=>'</tt>', + '<smaller>'=>'<font size=-1>','</smaller>'=>'</font>', + '<bigger>'=>'<font size=+1>','</bigger>'=>'</font>', + '<underline>'=>'<span style="text-decoration: underline">', + '</underline>'=>'</span>', + '<flushleft>'=>'<span style="text-align:left">', + '</flushleft>'=>'</span>', + '<flushright>'=>'<span style="text-align:right">', + '</flushright>'=>'</span>', + '<flushboth>'=>'<span style="text-align:justified">', + '</flushboth>'=>'</span>', + '<indent>'=>'<span style="padding-left: 20px">', + '</indent>'=>'</span>', + '<indentright>'=>'<span style="padding-right: 20px">', + '</indentright>'=>'</span>'); + + while(list($find,$replace)=each($a)){ + $body = eregi_replace($find,$replace,$body); + } + return $body; +} + +function enriched_font($body){ + $pattern = '/(.*)\<fontfamily\>\<param\>(.*)\<\/param\>(.*)\<\/fontfamily\>(.*)/ims'; + while(preg_match($pattern,$body,$a)){ + //print_r($a); + if (count($a)!=5) continue; + $body=$a[1].'<span style="font-family: '.$a[2].'">'.$a[3].'</span>'.$a[4]; + } + + return $body; +} + + +function enriched_color($body){ + $pattern = '/(.*)\<color\>\<param\>(.*)\<\/param\>(.*)\<\/color\>(.*)/ims'; + while(preg_match($pattern,$body,$a)){ + //print_r($a); + if (count($a)!=5) continue; + + //extract color (either by name, or ####,####,####) + if (strpos($a[2],',')){ + $rgb = explode(',',$a[2]); + $color ='#'; + for($i=0;$i<3;$i++) $color.=substr($rgb[$i],0,2); //just take first 2 bytes + }else{ + $color = $a[2]; + } + + //put it all together + $body = $a[1].'<span style="color: '.$color.'">'.$a[3].'</span>'.$a[4]; + } + + return $body; +} + +function enriched_excerpt($body){ + + $pattern = '/(.*)\<excerpt\>(.*)\<\/excerpt\>(.*)/i'; + while(preg_match($pattern,$body,$a)){ + //print_r($a); + if (count($a)!=4) continue; + $quoted = ''; + $lines = explode('<br>',$a[2]); + foreach($lines as $n=>$line) $quoted.='>'.$line.'<br>'; + $body=$a[1].'<span class="quotes">'.$quoted.'</span>'.$a[3]; + } + + return $body; +} + +function enriched_to_html($body){ + $body = str_replace('<<','<',$body); + $body = enriched_convert_newlines($body); + $body = str_replace("\n", '<br>', $body); + $body = enriched_convert_formatting($body); + $body = enriched_color($body); + $body = enriched_font($body); + $body = enriched_excerpt($body); + //$body = nl2br($body); + return $body; +} + +?>
\ No newline at end of file diff --git a/program/lib/html2text.inc b/program/lib/html2text.inc new file mode 100644 index 000000000..82a254e56 --- /dev/null +++ b/program/lib/html2text.inc @@ -0,0 +1,440 @@ +<?php + +/************************************************************************* +* * +* class.html2text.inc * +* * +************************************************************************* +* * +* Converts HTML to formatted plain text * +* * +* Copyright (c) 2005 Jon Abernathy <jon@chuggnutt.com> * +* All rights reserved. * +* * +* This script is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* The GNU General Public License can be found at * +* http://www.gnu.org/copyleft/gpl.html. * +* * +* This script is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* Author(s): Jon Abernathy <jon@chuggnutt.com> * +* * +* Last modified: 04/06/05 * +* Modified: 2004/05/19 (tbr) * +* * +*************************************************************************/ + + +/** +* Takes HTML and converts it to formatted, plain text. +* +* Thanks to Alexander Krug (http://www.krugar.de/) to pointing out and +* correcting an error in the regexp search array. Fixed 7/30/03. +* +* Updated set_html() function's file reading mechanism, 9/25/03. +* +* Thanks to Joss Sanglier (http://www.dancingbear.co.uk/) for adding +* several more HTML entity codes to the $search and $replace arrays. +* Updated 11/7/03. +* +* Thanks to Darius Kasperavicius (http://www.dar.dar.lt/) for +* suggesting the addition of $allowed_tags and its supporting function +* (which I slightly modified). Updated 3/12/04. +* +* Thanks to Justin Dearing for pointing out that a replacement for the +* <TH> tag was missing, and suggesting an appropriate fix. +* Updated 8/25/04. +* +* Thanks to Mathieu Collas (http://www.myefarm.com/) for finding a +* display/formatting bug in the _build_link_list() function: email +* readers would show the left bracket and number ("[1") as part of the +* rendered email address. +* Updated 12/16/04. +* +* Thanks to Wojciech Bajon (http://histeria.pl/) for submitting code +* to handle relative links, which I hadn't considered. I modified his +* code a bit to handle normal HTTP links and MAILTO links. Also for +* suggesting three additional HTML entity codes to search for. +* Updated 03/02/05. +* +* Thanks to Jacob Chandler for pointing out another link condition +* for the _build_link_list() function: "https". +* Updated 04/06/05. +* +* @author Jon Abernathy <jon@chuggnutt.com> +* @version 0.6.1 +* @since PHP 4.0.2 +*/ +class html2text +{ + + /** + * Contains the HTML content to convert. + * + * @var string $html + * @access public + */ + var $html; + + /** + * Contains the converted, formatted text. + * + * @var string $text + * @access public + */ + var $text; + + /** + * Maximum width of the formatted text, in columns. + * + * @var integer $width + * @access public + */ + var $width = 70; + + /** + * List of preg* regular expression patterns to search for, + * used in conjunction with $replace. + * + * @var array $search + * @access public + * @see $replace + */ + var $search = array( + "/\r/", // Non-legal carriage return + "/[\n\t]+/", // Newlines and tabs + '/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with + //'/<!-- .* -->/', // Comments -- which strip_tags might have problem a with + '/<a href="([^"]+)"[^>]*>(.+?)<\/a>/ie', // <a href=""> + '/<h[123][^>]*>(.+?)<\/h[123]>/ie', // H1 - H3 + '/<h[456][^>]*>(.+?)<\/h[456]>/ie', // H4 - H6 + '/<p[^>]*>/i', // <P> + '/<br[^>]*>/i', // <br> + '/<b[^>]*>(.+?)<\/b>/ie', // <b> + '/<i[^>]*>(.+?)<\/i>/i', // <i> + '/(<ul[^>]*>|<\/ul>)/i', // <ul> and </ul> + '/(<ol[^>]*>|<\/ol>)/i', // <ol> and </ol> + '/<li[^>]*>/i', // <li> + '/<hr[^>]*>/i', // <hr> + '/(<table[^>]*>|<\/table>)/i', // <table> and </table> + '/(<tr[^>]*>|<\/tr>)/i', // <tr> and </tr> + '/<td[^>]*>(.+?)<\/td>/i', // <td> and </td> + '/<th[^>]*>(.+?)<\/th>/i', // <th> and </th> + '/ /i', + '/"/i', + '/>/i', + '/</i', + '/&/i', + '/©/i', + '/™/i', + '/“/', + '/”/', + '/–/', + '/’/', + '/&/', + '/©/', + '/™/', + '/—/', + '/“/', + '/”/', + '/•/', + '/®/i', + '/•/i', + '/&[&;]+;/i' + ); + + /** + * List of pattern replacements corresponding to patterns searched. + * + * @var array $replace + * @access public + * @see $search + */ + var $replace = array( + '', // Non-legal carriage return + ' ', // Newlines and tabs + '', // <script>s -- which strip_tags supposedly has problems with + //'', // Comments -- which strip_tags might have problem a with + '$this->_build_link_list("\\1", "\\2")', // <a href=""> + "strtoupper(\"\n\n\\1\n\n\")", // H1 - H3 + "ucwords(\"\n\n\\1\n\n\")", // H4 - H6 + "\n\n\t", // <P> + "\n", // <br> + 'strtoupper("\\1")', // <b> + '_\\1_', // <i> + "\n\n", // <ul> and </ul> + "\n\n", // <ol> and </ol> + "\t*", // <li> + "\n-------------------------\n", // <hr> + "\n\n", // <table> and </table> + "\n", // <tr> and </tr> + "\t\t\\1\n", // <td> and </td> + "strtoupper(\"\t\t\\1\n\")", // <th> and </th> + ' ', + '"', + '>', + '<', + '&', + '(c)', + '(tm)', + '"', + '"', + '-', + "'", + '&', + '(c)', + '(tm)', + '--', + '"', + '"', + '*', + '(R)', + '*', + '' + ); + + /** + * Contains a list of HTML tags to allow in the resulting text. + * + * @var string $allowed_tags + * @access public + * @see set_allowed_tags() + */ + var $allowed_tags = ''; + + /** + * Contains the base URL that relative links should resolve to. + * + * @var string $url + * @access public + */ + var $url; + + /** + * Indicates whether content in the $html variable has been converted yet. + * + * @var boolean $converted + * @access private + * @see $html, $text + */ + var $_converted = false; + + /** + * Contains URL addresses from links to be rendered in plain text. + * + * @var string $link_list + * @access private + * @see _build_link_list() + */ + var $_link_list = array(); + + /** + * Constructor. + * + * If the HTML source string (or file) is supplied, the class + * will instantiate with that source propagated, all that has + * to be done it to call get_text(). + * + * @param string $source HTML content + * @param boolean $from_file Indicates $source is a file to pull content from + * @access public + * @return void + */ + function html2text( $source = '', $from_file = false ) + { + if ( !empty($source) ) { + $this->set_html($source, $from_file); + } + $this->set_base_url(); + } + + /** + * Loads source HTML into memory, either from $source string or a file. + * + * @param string $source HTML content + * @param boolean $from_file Indicates $source is a file to pull content from + * @access public + * @return void + */ + function set_html( $source, $from_file = false ) + { + $this->html = $source; + + if ( $from_file && file_exists($source) ) { + $fp = fopen($source, 'r'); + $this->html = fread($fp, filesize($source)); + fclose($fp); + } + + $this->_converted = false; + } + + /** + * Returns the text, converted from HTML. + * + * @access public + * @return string + */ + function get_text() + { + if ( !$this->_converted ) { + $this->_convert(); + } + + return $this->text; + } + + /** + * Prints the text, converted from HTML. + * + * @access public + * @return void + */ + function print_text() + { + print $this->get_text(); + } + + /** + * Alias to print_text(), operates identically. + * + * @access public + * @return void + * @see print_text() + */ + function p() + { + print $this->get_text(); + } + + /** + * Sets the allowed HTML tags to pass through to the resulting text. + * + * Tags should be in the form "<p>", with no corresponding closing tag. + * + * @access public + * @return void + */ + function set_allowed_tags( $allowed_tags = '' ) + { + if ( !empty($allowed_tags) ) { + $this->allowed_tags = $allowed_tags; + } + } + + /** + * Sets a base URL to handle relative links. + * + * @access public + * @return void + */ + function set_base_url( $url = '' ) + { + if ( empty($url) ) { + $this->url = 'http://' . $_SERVER['HTTP_HOST']; + } else { + // Strip any trailing slashes for consistency (relative + // URLs may already start with a slash like "/file.html") + if ( substr($url, -1) == '/' ) { + $url = substr($url, 0, -1); + } + $this->url = $url; + } + } + + /** + * Workhorse function that does actual conversion. + * + * First performs custom tag replacement specified by $search and + * $replace arrays. Then strips any remaining HTML tags, reduces whitespace + * and newlines to a readable format, and word wraps the text to + * $width characters. + * + * @access private + * @return void + */ + function _convert() + { + // Variables used for building the link list + //$link_count = 1; + //$this->_link_list = ''; + + $text = trim(stripslashes($this->html)); + + // Run our defined search-and-replace + $text = preg_replace($this->search, $this->replace, $text); + + // Strip any other HTML tags + $text = strip_tags($text, $this->allowed_tags); + + // Bring down number of empty lines to 2 max + $text = preg_replace("/\n\s+\n/", "\n", $text); + $text = preg_replace("/[\n]{3,}/", "\n\n", $text); + + // Add link list + if ( sizeof($this->_link_list) ) { + $text .= "\n\nLinks:\n------\n"; + foreach ($this->_link_list as $id => $link) { + $text .= '[' . ($id+1) . '] ' . $link . "\n"; + } + } + + // Wrap the text to a readable format + // for PHP versions >= 4.0.2. Default width is 75 + $text = wordwrap($text, $this->width); + + $this->text = $text; + + $this->_converted = true; + } + + /** + * Helper function called by preg_replace() on link replacement. + * + * Maintains an internal list of links to be displayed at the end of the + * text, with numeric indices to the original point in the text they + * appeared. Also makes an effort at identifying and handling absolute + * and relative links. + * + * @param integer $link_count Counter tracking current link number + * @param string $link URL of the link + * @param string $display Part of the text to associate number with + * @access private + * @return string + */ + function _build_link_list($link, $display) + { + $link_lc = strtolower($link); + + if (substr($link_lc, 0, 7) == 'http://' || substr($link_lc, 0, 8) == 'https://' || substr($link_lc, 0, 7) == 'mailto:') + { + $url = $link; + } + else + { + $url = $this->url; + if ($link{0} != '/') { + $url .= '/'; + } + $url .= $link; + } + + $index = array_search($url, $this->_link_list); + if ($index===FALSE) + { + $index = sizeof($this->_link_list); + $this->_link_list[$index] = $url; + } + + return $display . ' [' . ($index+1) . ']'; + } +} + +?>
\ No newline at end of file diff --git a/program/lib/icl_commons.inc b/program/lib/icl_commons.inc new file mode 100644 index 000000000..599205178 --- /dev/null +++ b/program/lib/icl_commons.inc @@ -0,0 +1,81 @@ +<?php +function mod_b64_decode($data){ + return base64_decode(str_replace(",","/",$data)); +} + +function mod_b64_encode($data){ + return str_replace("/",",",str_replace("=","",base64_encode($data))); +} + + +function utf8_to_html($str){ + $len = strlen($str); + $out = ""; + for($i=0;$i<$len;$i+=2){ + $val = ord($str[$i]); + $next_val = ord($str[$i+1]); + if ($val<255){ + $out.="&#".($val*256+$next_val).";"; + }else{ + $out.=$str[$i].$str[$i+1]; + } + } + return $out; +} + +function iil_utf7_decode($str, $raw=false){ + if (strpos($str, '&')===false) return $str; + + $len = strlen($str); + $in_b64 = false; + $b64_data = ""; + $out = ""; + for ($i=0;$i<$len;$i++){ + $char = $str[$i]; + if ($char=='&') $in_b64 = true; + else if ($in_b64 && $char=='-'){ + $in_b64 = false; + if ($b64_data=="") $out.="&"; + else{ + $dec=mod_b64_decode($b64_data); + $out.=($raw?$dec:utf8_to_html($dec)); + $b64_data = ""; + } + }else if ($in_b64) $b64_data.=$char; + else $out.=$char; + } + return $out; +} + +function iil_utf7_encode($str){ + if (!ereg("[\200-\237]",$str) && !ereg("[\241-\377]",$str)) + return $str; + + $len = strlen($str); + + for ($i=0;$i<$len;$i++){ + $val = ord($str[$i]); + if ($val>=224 && $val<=239){ + $unicode = ($val-224) * 4096 + (ord($str[$i+1])-128) * 64 + (ord($str[$i+2])-128); + $i+=2; + $utf_code.=chr((int)($unicode/256)).chr($unicode%256); + }else if ($val>=192 && $val<=223){ + $unicode = ($val-192) * 64 + (ord($str[$i+1])-128); + $i++; + $utf_code.=chr((int)($unicode/256)).chr($unicode%256); + }else{ + if ($utf_code){ + $out.='&'.mod_b64_encode($utf_code).'-'; + $utf_code=""; + } + if ($str[$i]=="-") $out.="&"; + $out.=$str[$i]; + } + } + if ($utf_code) + $out.='&'.mod_b64_encode($utf_code).'-'; + return $out; +} + + +?>
\ No newline at end of file diff --git a/program/lib/imap.inc b/program/lib/imap.inc new file mode 100644 index 000000000..53a518bee --- /dev/null +++ b/program/lib/imap.inc @@ -0,0 +1,2038 @@ +<?php +///////////////////////////////////////////////////////// +// +// Iloha IMAP Library (IIL) +// +// (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org> +// +// This file is part of IlohaMail. IlohaMail is free software released +// under the GPL license. See enclosed file COPYING for details, or +// see http://www.fsf.org/copyleft/gpl.html +// +///////////////////////////////////////////////////////// + +/******************************************************** + + FILE: include/imap.inc + PURPOSE: + Provide alternative IMAP library that doesn't rely on the standard + C-Client based version. This allows IlohaMail to function regardless + of whether or not the PHP build it's running on has IMAP functionality + built-in. + USEAGE: + Function containing "_C_" in name require connection handler to be + passed as one of the parameters. To obtain connection handler, use + iil_Connect() + +********************************************************/ + +// changed path to work within roundcube webmail +include_once("lib/icl_commons.inc"); + + +if (!$IMAP_USE_HEADER_DATE) $IMAP_USE_INTERNAL_DATE = true; +$IMAP_MONTHS=array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12); +$IMAP_SERVER_TZ = date('Z'); + +$iil_error; +$iil_errornum; +$iil_selected; + +class iilConnection{ + var $fp; + var $error; + var $errorNum; + var $selected; + var $message; + var $host; + var $cache; + var $uid_cache; + var $do_cache; + var $exists; + var $recent; + var $rootdir; + var $delimiter; +} + +class iilBasicHeader{ + var $id; + var $uid; + var $subject; + var $from; + var $to; + var $cc; + var $replyto; + var $in_reply_to; + var $date; + var $messageID; + var $size; + var $encoding; + var $ctype; + var $flags; + var $timestamp; + var $f; + var $seen; + var $deleted; + var $recent; + var $answered; + var $junk; + var $internaldate; + var $is_reply; +} + + +class iilThreadHeader{ + var $id; + var $sbj; + var $irt; + var $mid; +} + + +function iil_xor($string, $string2){ + $result = ""; + $size = strlen($string); + for ($i=0; $i<$size; $i++) $result .= chr(ord($string[$i]) ^ ord($string2[$i])); + + return $result; +} + +function iil_ReadLine($fp, $size){ + $line=""; + if ($fp){ + do{ + $buffer = fgets($fp, 2048); + $line.=$buffer; + }while($buffer[strlen($buffer)-1]!="\n"); + } + return $line; +} + +function iil_MultLine($fp, $line){ + $line = chop($line); + if (ereg('\{[0-9]+\}$', $line)){ + $out = ""; + preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a); + $bytes = $a[2][0]; + while(strlen($out)<$bytes){ + $out.=chop(iil_ReadLine($fp, 1024)); + } + $line = $a[1][0]."\"$out\""; + } + return $line; +} + +function iil_ReadBytes($fp, $bytes){ + $data = ""; + $len = 0; + do{ + $data.=fread($fp, $bytes-$len); + $len = strlen($data); + }while($len<$bytes); + return $data; +} + +function iil_ReadReply($fp){ + do{ + $line = chop(trim(iil_ReadLine($fp, 1024))); + }while($line[0]=="*"); + + return $line; +} + +function iil_ParseResult($string){ + $a=explode(" ", $string); + if (count($a) > 2){ + if (strcasecmp($a[1], "OK")==0) return 0; + else if (strcasecmp($a[1], "NO")==0) return -1; + else if (strcasecmp($a[1], "BAD")==0) return -2; + }else return -3; +} + +// check if $string starts with $match +function iil_StartsWith($string, $match){ + $len = strlen($match); + if ($len==0) return false; + if (strncmp($string, $match, $len)==0) return true; + else return false; +} + +function iil_StartsWithI($string, $match){ + $len = strlen($match); + if ($len==0) return false; + if (strncasecmp($string, $match, $len)==0) return true; + else return false; +} + + +function iil_C_Authenticate(&$conn, $user, $pass, $encChallenge){ + + // initialize ipad, opad + for ($i=0;$i<64;$i++){ + $ipad.=chr(0x36); + $opad.=chr(0x5C); + } + // pad $pass so it's 64 bytes + $padLen = 64 - strlen($pass); + for ($i=0;$i<$padLen;$i++) $pass .= chr(0); + // generate hash + $hash = md5(iil_xor($pass,$opad).pack("H*",md5(iil_xor($pass, $ipad).base64_decode($encChallenge)))); + // generate reply + $reply = base64_encode($user." ".$hash); + + // send result, get reply + fputs($conn->fp, $reply."\r\n"); + $line = iil_ReadLine($conn->fp, 1024); + + // process result + if (iil_ParseResult($line)==0){ + $conn->error .= ""; + $conn->errorNum = 0; + return $conn->fp; + }else{ + $conn->error .= 'Authentication failed (AUTH): <br>"'.htmlspecialchars($line)."\""; + $conn->errorNum = -2; + return false; + } +} + +function iil_C_Login(&$conn, $user, $password){ + + fputs($conn->fp, "a001 LOGIN $user \"$password\"\r\n"); + + do{ + $line = iil_ReadReply($conn->fp); + }while(!iil_StartsWith($line, "a001 ")); + $a=explode(" ", $line); + if (strcmp($a[1],"OK")==0){ + $result=$conn->fp; + $conn->error.=""; + $conn->errorNum = 0; + }else{ + $result=false; + fclose($conn->fp); + $conn->error .= 'Authentication failed (LOGIN):<br>"'.htmlspecialchars($line)."\""; + $conn->errorNum = -2; + } + return $result; +} + +function iil_ParseNamespace2($str, &$i, $len=0, $l){ + if (!$l) $str = str_replace("NIL", "()", $str); + if (!$len) $len = strlen($str); + $data = array(); + $in_quotes = false; + $elem = 0; + for($i;$i<$len;$i++){ + $c = (string)$str[$i]; + if ($c=='(' && !$in_quotes){ + $i++; + $data[$elem] = iil_ParseNamespace2($str, $i, $len, $l++); + $elem++; + }else if ($c==')' && !$in_quotes) return $data; + else if ($c=="\\"){ + $i++; + if ($in_quotes) $data[$elem].=$c.$str[$i]; + }else if ($c=='"'){ + $in_quotes = !$in_quotes; + if (!$in_quotes) $elem++; + }else if ($in_quotes){ + $data[$elem].=$c; + } + } + return $data; +} + +function iil_C_NameSpace(&$conn){ + global $my_prefs; + + if ($my_prefs["rootdir"]) return true; + + fputs($conn->fp, "ns1 NAMESPACE\r\n"); + do{ + $line = iil_ReadLine($conn->fp, 1024); + if (iil_StartsWith($line, "* NAMESPACE")){ + $i = 0; + $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0); + } + }while(!iil_StartsWith($line, "ns1")); + + if (!is_array($data)) return false; + + $user_space_data = $data[0]; + if (!is_array($user_space_data)) return false; + + $first_userspace = $user_space_data[0]; + if (count($first_userspace)!=2) return false; + + $conn->rootdir = $first_userspace[0]; + $conn->delimiter = $first_userspace[1]; + $my_prefs["rootdir"] = substr($conn->rootdir, 0, -1); + + return true; + +} + +function iil_Connect($host, $user, $password){ + global $iil_error, $iil_errornum; + global $ICL_SSL, $ICL_PORT; + global $IMAP_NO_CACHE; + global $my_prefs, $IMAP_USE_INTERNAL_DATE; + + $iil_error = ""; + $iil_errornum = 0; + + //strip slashes + $user = stripslashes($user); + $password = stripslashes($password); + + //set auth method + $auth_method = "plain"; + if (func_num_args() >= 4){ + $auth_array = func_get_arg(3); + if (is_array($auth_array)) $auth_method = $auth_array["imap"]; + if (empty($auth_method)) $auth_method = "plain"; + } + $message = "INITIAL: $auth_method\n"; + + $result = false; + + //initialize connection + $conn = new iilConnection; + $conn->error=""; + $conn->errorNum=0; + $conn->selected=""; + $conn->user = $user; + $conn->host = $host; + $conn->cache = array(); + $conn->do_cache = (function_exists("cache_write")&&!$IMAP_NO_CACHE); + $conn->cache_dirty = array(); + + if ($my_prefs['sort_field']=='INTERNALDATE') $IMAP_USE_INTERNAL_DATE = true; + else if ($my_prefs['sort_field']=='DATE') $IMAP_USE_INTERNAL_DATE = false; + //echo '<!-- conn sort_field: '.$my_prefs['sort_field'].' //-->'; + + //check input + if (empty($host)) $iil_error .= "Invalid host<br>\n"; + if (empty($user)) $iil_error .= "Invalid user<br>\n"; + if (empty($password)) $iil_error .= "Invalid password<br>\n"; + if (!empty($iil_error)) return false; + if (!$ICL_PORT) $ICL_PORT = 143; + + //check for SSL + if ($ICL_SSL){ + $host = "ssl://".$host; + } + + //open socket connection + $conn->fp = @fsockopen($host, $ICL_PORT); + if (!$conn->fp){ + $iil_error = "Could not connect to $host at port $ICL_PORT"; + $iil_errornum = -1; + return false; + } + + $iil_error.="Socket connection established\r\n"; + $line=iil_ReadLine($conn->fp, 300); + + if (strcasecmp($auth_method, "check")==0){ + //check for supported auth methods + + //default to plain text auth + $auth_method = "plain"; + + //check for CRAM-MD5 + fputs($conn->fp, "cp01 CAPABILITY\r\n"); + do{ + $line = trim(chop(iil_ReadLine($conn->fp, 100))); + $a = explode(" ", $line); + if ($line[0]=="*"){ + while ( list($k, $w) = each($a) ){ + if ((strcasecmp($w, "AUTH=CRAM_MD5")==0)|| + (strcasecmp($w, "AUTH=CRAM-MD5")==0)){ + $auth_method = "auth"; + } + } + } + }while($a[0]!="cp01"); + } + + if (strcasecmp($auth_method, "auth")==0){ + $conn->message.="Trying CRAM-MD5\n"; + //do CRAM-MD5 authentication + fputs($conn->fp, "a000 AUTHENTICATE CRAM-MD5\r\n"); + $line = trim(chop(iil_ReadLine($conn->fp, 1024))); + if ($line[0]=="+"){ + $conn->message.='Got challenge: '.htmlspecialchars($line)."\n"; + //got a challenge string, try CRAM-5 + $result = iil_C_Authenticate($conn, $user, $password, substr($line,2)); + $conn->message.= "Tried CRAM-MD5: $result \n"; + }else{ + $conn->message.='No challenge ('.htmlspecialchars($line)."), try plain\n"; + $auth = "plain"; + } + } + + if ((!$result)||(strcasecmp($auth, "plain")==0)){ + //do plain text auth + $result = iil_C_Login($conn, $user, $password); + $conn->message.="Tried PLAIN: $result \n"; + } + + $conn->message .= $auth; + + if ($result){ + iil_C_Namespace($conn); + return $conn; + }else{ + $iil_error = $conn->error; + $iil_errornum = $conn->errorNum; + return false; + } +} + +function iil_Close(&$conn){ + iil_C_WriteCache($conn); + if (@fputs($conn->fp, "I LOGOUT\r\n")){ + fgets($conn->fp, 1024); + fclose($conn->fp); + $conn->fp = false; + } +} + +function iil_ClearCache($user, $host){ +} + + +function iil_C_WriteCache(&$conn){ + //echo "<!-- doing iil_C_WriteCache //-->\n"; + if (!$conn->do_cache) return false; + + if (is_array($conn->cache)){ + while(list($folder,$data)=each($conn->cache)){ + if ($folder && is_array($data) && $conn->cache_dirty[$folder]){ + $key = $folder.".imap"; + $result = cache_write($conn->user, $conn->host, $key, $data, true); + //echo "<!-- writing $key $data: $result //-->\n"; + } + } + } +} + +function iil_C_EnableCache(&$conn){ + $conn->do_cache = true; +} + +function iil_C_DisableCache(&$conn){ + $conn->do_cache = false; +} + +function iil_C_LoadCache(&$conn, $folder){ + if (!$conn->do_cache) return false; + + $key = $folder.".imap"; + if (!is_array($conn->cache[$folder])){ + $conn->cache[$folder] = cache_read($conn->user, $conn->host, $key); + $conn->cache_dirty[$folder] = false; + } +} + +function iil_C_ExpireCachedItems(&$conn, $folder, $message_set){ + + if (!$conn->do_cache) return; //caching disabled + if (!is_array($conn->cache[$folder])) return; //cache not initialized|empty + if (count($conn->cache[$folder])==0) return; //cache not initialized|empty + + $uids = iil_C_FetchHeaderIndex($conn, $folder, $message_set, "UID"); + $num_removed = 0; + if (is_array($uids)){ + //echo "<!-- unsetting: ".implode(",",$uids)." //-->\n"; + while(list($n,$uid)=each($uids)){ + unset($conn->cache[$folder][$uid]); + //$conn->cache[$folder][$uid] = false; + //$num_removed++; + } + $conn->cache_dirty[$folder] = true; + + //echo '<!--'."\n"; + //print_r($conn->cache); + //echo "\n".'//-->'."\n"; + }else{ + echo "<!-- failed to get uids: $message_set //-->\n"; + } + + /* + if ($num_removed>0){ + $new_cache; + reset($conn->cache[$folder]); + while(list($uid,$item)=each($conn->cache[$folder])){ + if ($item) $new_cache[$uid] = $conn->cache[$folder][$uid]; + } + $conn->cache[$folder] = $new_cache; + } + */ +} + +function iil_ExplodeQuotedString($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; +} + +function iil_CheckForRecent($host, $user, $password, $mailbox){ + if (empty($mailbox)) $mailbox="INBOX"; + + $conn=iil_Connect($host, $user, $password, "plain"); + $fp = $conn->fp; + if ($fp){ + fputs($fp, "a002 EXAMINE \"$mailbox\"\r\n"); + do{ + $line=chop(iil_ReadLine($fp, 300)); + $a=explode(" ", $line); + if (($a[0]=="*") && (strcasecmp($a[2], "RECENT")==0)) $result=(int)$a[1]; + }while (!iil_StartsWith($a[0],"a002")); + + fputs($fp, "a003 LOGOUT\r\n"); + fclose($fp); + }else $result=-2; + + return $result; +} + +function iil_C_Select(&$conn, $mailbox){ + $fp = $conn->fp; + + if (empty($mailbox)) return false; + if (strcmp($conn->selected, $mailbox)==0) return true; + + iil_C_LoadCache($conn, $mailbox); + + if (fputs($fp, "sel1 SELECT \"$mailbox\"\r\n")){ + do{ + $line=chop(iil_ReadLine($fp, 300)); + $a=explode(" ", $line); + if (count($a) == 3){ + if (strcasecmp($a[2], "EXISTS")==0) $conn->exists=(int)$a[1]; + if (strcasecmp($a[2], "RECENT")==0) $conn->recent=(int)$a[1]; + } + }while (!iil_StartsWith($line, "sel1")); + + $a=explode(" ", $line); + + if (strcasecmp($a[1],"OK")==0){ + $conn->selected = $mailbox; + return true; + }else return false; + }else{ + return false; + } +} + +function iil_C_CheckForRecent(&$conn, $mailbox){ + if (empty($mailbox)) $mailbox="INBOX"; + + iil_C_Select($conn, $mailbox); + if ($conn->selected==$mailbox) return $conn->recent; + else return false; +} + +function iil_C_CountMessages(&$conn, $mailbox, $refresh=false){ + if ($refresh) $conn->selected=""; + iil_C_Select($conn, $mailbox); + if ($conn->selected==$mailbox) return $conn->exists; + else return false; +} + +function iil_SplitHeaderLine($string){ + $pos=strpos($string, ":"); + if ($pos>0){ + $res[0]=substr($string, 0, $pos); + $res[1]=trim(substr($string, $pos+1)); + return $res; + }else{ + return $string; + } +} + +function iil_StrToTime($str){ + global $IMAP_MONTHS,$IMAP_SERVER_TZ; + + if ($str) $time1 = strtotime($str); + if ($time1 && $time1!=-1) return $time1-$IMAP_SERVER_TZ; + + //echo '<!--'.$str.'//-->'; + + //replace double spaces with single space + $str = trim($str); + $str = str_replace(" ", " ", $str); + + //strip off day of week + $pos=strpos($str, " "); + if (!is_numeric(substr($str, 0, $pos))) $str = substr($str, $pos+1); + + //explode, take good parts + $a=explode(" ",$str); + //$month_a=array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12); + $month_str=$a[1]; + $month=$IMAP_MONTHS[$month_str]; + $day=$a[0]; + $year=$a[2]; + $time=$a[3]; + $tz_str = $a[4]; + $tz = substr($tz_str, 0, 3); + $ta=explode(":",$time); + $hour=(int)$ta[0]-(int)$tz; + $minute=$ta[1]; + $second=$ta[2]; + + //make UNIX timestamp + $time2 = mktime($hour, $minute, $second, $month, $day, $year); + //echo '<!--'.$time1.' '.$time2.' //-->'."\n"; + return $time2; +} + +function iil_C_Sort(&$conn, $mailbox, $field){ + /* Do "SELECT" command */ + if (!iil_C_Select($conn, $mailbox)) return false; + + $field = strtoupper($field); + if ($field=='INTERNALDATE') $field='ARRIVAL'; + $fields = array('ARRIVAL'=>1,'CC'=>1,'DATE'=>1,'FROM'=>1,'SIZE'=>1,'SUBJECT'=>1,'TO'=>1); + + if (!$fields[$field]) return false; + + $fp = $conn->fp; + $command = 's SORT ('.$field.') US-ASCII ALL'."\r\n"; + $line = $data = ''; + + if (!fputs($fp, $command)) return false; + do{ + $line = chop(iil_ReadLine($fp, 1024)); + if (iil_StartsWith($line, '* SORT')) $data.=($data?' ':'').substr($line,7); + }while($line[0]!='s'); + + if (empty($data)){ + $conn->error = $line; + return false; + } + + $out = explode(' ',$data); + return $out; +} + +function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field,$normalize=true){ + global $IMAP_USE_INTERNAL_DATE; + + $c=0; + $result=array(); + $fp = $conn->fp; + + if (empty($index_field)) $index_field="DATE"; + $index_field = strtoupper($index_field); + + if (empty($message_set)) return array(); + + //$fields_a["DATE"] = ($IMAP_USE_INTERNAL_DATE?6:1); + $fields_a['DATE'] = 1; + $fields_a['INTERNALDATE'] = 6; + $fields_a['FROM'] = 1; + $fields_a['REPLY-TO'] = 1; + $fields_a['SENDER'] = 1; + $fields_a['TO'] = 1; + $fields_a['SUBJECT'] = 1; + $fields_a['UID'] = 2; + $fields_a['SIZE'] = 2; + $fields_a['SEEN'] = 3; + $fields_a['RECENT'] = 4; + $fields_a['DELETED'] = 5; + + $mode=$fields_a[$index_field]; + if (!($mode > 0)) return false; + + /* Do "SELECT" command */ + if (!iil_C_Select($conn, $mailbox)) return false; + + /* FETCH date,from,subject headers */ + if ($mode==1){ + $key="fhi".($c++); + $request=$key." FETCH $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)])\r\n"; + if (!fputs($fp, $request)) return false; + do{ + + $line=chop(iil_ReadLine($fp, 200)); + $a=explode(" ", $line); + if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[strlen($line)-1]!=")")){ + $id=$a[1]; + + $str=$line=chop(iil_ReadLine($fp, 300)); + + while($line[0]!=")"){ //caution, this line works only in this particular case + $line=chop(iil_ReadLine($fp, 300)); + if ($line[0]!=")"){ + if (ord($line[0]) <= 32){ //continuation from previous header line + $str.=" ".trim($line); + } + if ((ord($line[0]) > 32) || (strlen($line[0]) == 0)){ + list($field, $string) = iil_SplitHeaderLine($str); + if (strcasecmp($field, "date")==0){ + $result[$id]=iil_StrToTime($string); + }else{ + $result[$id] = str_replace("\"", "", $string); + if ($normalize) $result[$id]=strtoupper($result[$id]); + } + $str=$line; + } + } + } + } + /* + $end_pos = strlen($line)-1; + if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[$end_pos]=="}")){ + $id = $a[1]; + $pos = strrpos($line, "{")+1; + $bytes = (int)substr($line, $pos, $end_pos-$pos); + $received = 0; + do{ + $line = iil_ReadLine($fp, 0); + $received+=strlen($line); + $line = chop($line); + + if ($received>$bytes) break; + else if (!$line) continue; + + list($field,$string)=explode(": ", $line); + + if (strcasecmp($field, "date")==0) + $result[$id] = iil_StrToTime($string); + else if ($index_field!="DATE") + $result[$id]=strtoupper(str_replace("\"", "", $string)); + }while($line[0]!=")"); + }else{ + //one line response, not expected so ignore + } + */ + }while(!iil_StartsWith($line, $key)); + }else if ($mode==6){ + $key="fhi".($c++); + $request = $key." FETCH $message_set (INTERNALDATE)\r\n"; + if (!fputs($fp, $request)) return false; + do{ + $line=chop(iil_ReadLine($fp, 200)); + if ($line[0]=="*"){ + //original: "* 10 FETCH (INTERNALDATE "31-Jul-2002 09:18:02 -0500")" + $paren_pos = strpos($line, "("); + $foo = substr($line, 0, $paren_pos); + $a = explode(" ", $foo); + $id = $a[1]; + + $open_pos = strpos($line, "\"") + 1; + $close_pos = strrpos($line, "\""); + if ($open_pos && $close_pos){ + $len = $close_pos - $open_pos; + $time_str = substr($line, $open_pos, $len); + $result[$id] = strtotime($time_str); + } + }else{ + $a = explode(" ", $line); + } + }while(!iil_StartsWith($a[0], $key)); + }else{ + if ($mode >= 3) $field_name="FLAGS"; + else if ($index_field=="SIZE") $field_name="RFC822.SIZE"; + else $field_name=$index_field; + + /* FETCH uid, size, flags */ + $key="fhi".($c++); + $request=$key." FETCH $message_set ($field_name)\r\n"; + + if (!fputs($fp, $request)) return false; + do{ + $line=chop(iil_ReadLine($fp, 200)); + $a = explode(" ", $line); + if (($line[0]=="*") && ($a[2]=="FETCH")){ + $line=str_replace("(", "", $line); + $line=str_replace(")", "", $line); + $a=explode(" ", $line); + + $id=$a[1]; + + if (isset($result[$id])) continue; //if we already got the data, skip forward + if ($a[3]!=$field_name) continue; //make sure it's returning what we requested + + /* Caution, bad assumptions, next several lines */ + if ($mode==2) $result[$id]=$a[4]; + else{ + $haystack=strtoupper($line); + $result[$id]=(strpos($haystack, $index_field) > 0 ? "F" : "N"); + } + } + }while(!iil_StartsWith($line, $key)); + } + + //check number of elements... + list($start_mid,$end_mid)=explode(':',$message_set); + if (is_numeric($start_mid) && is_numeric($end_mid)){ + //count how many we should have + $should_have = $end_mid - $start_mid +1; + + //if we have less, try and fill in the "gaps" + if (count($result)<$should_have){ + for($i=$start_mid;$i<=$end_mid;$i++) if (!isset($result[$i])) $result[$i] = ''; + } + } + + return $result; + +} + +function iil_CompressMessageSet($message_set){ + //given a comma delimited list of independent mid's, + //compresses by grouping sequences together + + //if less than 255 bytes long, let's not bother + if (strlen($message_set)<255) return $message_set; + + //see if it's already been compress + if (strpos($message_set,':')!==false) return $message_set; + + //separate, then sort + $ids = explode(',',$message_set); + sort($ids); + + $result = array(); + $start = $prev = $ids[0]; + foreach($ids as $id){ + $incr = $id - $prev; + if ($incr>1){ //found a gap + if ($start==$prev) $result[] = $prev; //push single id + else $result[] = $start.':'.$prev; //push sequence as start_id:end_id + $start = $id; //start of new sequence + } + $prev = $id; + } + //handle the last sequence/id + if ($start==$prev) $result[] = $prev; + else $result[] = $start.':'.$prev; + + //return as comma separated string + return implode(',',$result); +} + +function iil_C_UIDsToMIDs(&$conn, $mailbox, $uids){ + if (!is_array($uids) || count($uids)==0) return array(); + return iil_C_Search($conn, $mailbox, "UID ".implode(",", $uids)); +} + +function iil_C_UIDToMID(&$conn, $mailbox, $uid){ + $result = iil_C_UIDsToMIDs($conn, $mailbox, array($uid)); + if (count($result)==1) return $result[0]; + else return false; +} + +function iil_C_FetchUIDs(&$conn,$mailbox){ + global $clock; + + $num = iil_C_CountMessages(&$conn, $mailbox); + if ($num==0) return array(); + $message_set = '1'.($num>1?':'.$num:''); + + //if cache not enabled, just call iil_C_FetchHeaderIndex on 'UID' field + if (!$conn->do_cache) + return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID'); + + //otherwise, let's check cache first + $key = $mailbox.'.uids'; + $cache_good = true; + if ($conn->uid_cache) $data = $conn->uid_cache; + else $data = cache_read($conn->user, $conn->host, $key); + + //was anything cached at all? + if ($data===false) $cache_good = -1; + + //make sure number of messages were the same + if ($cache_good>0 && $data['n']!=$num) $cache_good = -2; + + //if everything's okay so far... + if ($cache_good>0){ + //check UIDs of highest mid with current and cached + $temp = iil_C_Search($conn, $mailbox, 'UID '.$data['d'][$num]); + if (!$temp || !is_array($temp) || $temp[0]!=$num) $cache_good=-3; + } + + //if cached data's good, return it + if ($cache_good>0){ + return $data['d']; + } + + //otherwise, we need to fetch it + $data = array('n'=>$num,'d'=>array()); + $data['d'] = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID'); + cache_write($conn->user, $conn->host, $key, $data); + $conn->uid_cache = $data; + return $data['d']; +} + +function iil_SortThreadHeaders($headers, $index_a, $uids){ + asort($index_a); + $result = array(); + foreach($index_a as $mid=>$foobar){ + $uid = $uids[$mid]; + $result[$uid] = $headers[$uid]; + } + return $result; +} + +function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set){ + global $clock; + global $index_a; + + if (empty($message_set)) return false; + + $result = array(); + $uids = iil_C_FetchUIDs($conn, $mailbox); + $debug = false; + + /* Get cached records where possible */ + if ($conn->do_cache){ + $cached = cache_read($conn->user, $conn->host, $mailbox.'.thhd'); + if ($cached && is_array($uids) && count($uids)>0){ + $needed_set = ""; + foreach($uids as $id=>$uid){ + if ($cached[$uid]){ + $result[$uid] = $cached[$uid]; + $result[$uid]->id = $id; + }else $needed_set.=($needed_set?",":"").$id; + } + if ($needed_set) $message_set = $needed_set; + else $message_set = ''; + } + } + $message_set = iil_CompressMessageSet($message_set); + if ($debug) echo "Still need: ".$message_set; + + /* if we're missing any, get them */ + if ($message_set){ + /* FETCH date,from,subject headers */ + $key="fh"; + $fp = $conn->fp; + $request=$key." FETCH $message_set (BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])\r\n"; + $mid_to_id = array(); + if (!fputs($fp, $request)) return false; + do{ + $line = chop(iil_ReadLine($fp, 1024)); + if ($debug) echo $line."\n"; + if (ereg('\{[0-9]+\}$', $line)){ + $a = explode(" ", $line); + $new = array(); + + $new_thhd = new iilThreadHeader; + $new_thhd->id = $a[1]; + do{ + $line=chop(iil_ReadLine($fp, 1024),"\r\n"); + if (iil_StartsWithI($line,'Message-ID:') || (iil_StartsWithI($line,'In-Reply-To:')) || (iil_StartsWithI($line,'SUBJECT:'))){ + $pos = strpos($line, ":"); + $field_name = substr($line, 0, $pos); + $field_val = substr($line, $pos+1); + $new[strtoupper($field_name)] = trim($field_val); + }else if (ereg('^[[:space:]]', $line)){ + $new[strtoupper($field_name)].= trim($line); + } + }while($line[0]!=')'); + $new_thhd->sbj = $new['SUBJECT']; + $new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1); + $new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1); + + $result[$uids[$new_thhd->id]] = $new_thhd; + } + }while(!iil_StartsWith($line, "fh")); + } + + /* sort headers */ + if (is_array($index_a)){ + $result = iil_SortThreadHeaders($result, $index_a, $uids); + } + + /* write new set to cache */ + if ($conn->do_cache){ + if (count($result)!=count($cached)) + cache_write($conn->user, $conn->host, $mailbox.'.thhd', $result); + } + + //echo 'iil_FetchThreadHeaders:'."\n"; + //print_r($result); + + return $result; +} + +function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock){ + global $index_a; + + if (empty($message_set)) return false; + + $result=array(); + $roots=array(); + $root_mids = array(); + $sub_mids = array(); + $strays = array(); + $messages = array(); + $fp = $conn->fp; + $debug = false; + + $sbj_filter_pat = '[a-zA-Z]{2,3}(\[[0-9]*\])?:([[:space:]]*)'; + + /* Do "SELECT" command */ + if (!iil_C_Select($conn, $mailbox)) return false; + + /* FETCH date,from,subject headers */ + $mid_to_id = array(); + $messages = array(); + $headers = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set); + if ($clock) $clock->register('fetched headers'); + + if ($debug) print_r($headers); + + /* go through header records */ + foreach($headers as $header){ + //$id = $header['i']; + //$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'], + // 'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']); + $id = $header->id; + $new = array('id'=>$id, 'MESSAGE-ID'=>$header->mid, + 'IN-REPLY-TO'=>$header->irt, 'SUBJECT'=>$header->sbj); + + /* add to message-id -> mid lookup table */ + $mid_to_id[$new['MESSAGE-ID']] = $id; + + /* if no subject, use message-id */ + if (empty($new['SUBJECT'])) $new['SUBJECT'] = $new['MESSAGE-ID']; + + /* if subject contains 'RE:' or has in-reply-to header, it's a reply */ + $sbj_pre =''; + $has_re = false; + if (eregi($sbj_filter_pat, $new['SUBJECT'])) $has_re = true; + if ($has_re||$new['IN-REPLY-TO']) $sbj_pre = 'RE:'; + + /* strip out 're:', 'fw:' etc */ + if ($has_re) $sbj = ereg_replace($sbj_filter_pat,'', $new['SUBJECT']); + else $sbj = $new['SUBJECT']; + $new['SUBJECT'] = $sbj_pre.$sbj; + + + /* if subject not a known thread-root, add to list */ + if ($debug) echo $id.' '.$new['SUBJECT']."\t".$new['MESSAGE-ID']."\n"; + $root_id = $roots[$sbj]; + + if ($root_id && ($has_re || !$root_in_root[$root_id])){ + if ($debug) echo "\tfound root: $root_id\n"; + $sub_mids[$new['MESSAGE-ID']] = $root_id; + $result[$root_id][] = $id; + }else if (!isset($roots[$sbj])||(!$has_re&&$root_in_root[$root_id])){ + /* try to use In-Reply-To header to find root + unless subject contains 'Re:' */ + if ($has_re&&$new['IN-REPLY-TO']){ + if ($debug) echo "\tlooking: ".$new['IN-REPLY-TO']."\n"; + + //reply to known message? + $temp = $sub_mids[$new['IN-REPLY-TO']]; + + if ($temp){ + //found it, root:=parent's root + if ($debug) echo "\tfound parent: ".$new['SUBJECT']."\n"; + $result[$temp][] = $id; + $sub_mids[$new['MESSAGE-ID']] = $temp; + $sbj = ''; + }else{ + //if we can't find referenced parent, it's a "stray" + $strays[$id] = $new['IN-REPLY-TO']; + } + } + + //add subject as root + if ($sbj){ + if ($debug) echo "\t added to root\n"; + $roots[$sbj] = $id; + $root_in_root[$id] = !$has_re; + $sub_mids[$new['MESSAGE-ID']] = $id; + $result[$id] = array($id); + } + if ($debug) echo $new['MESSAGE-ID']."\t".$sbj."\n"; + } + + } + + //now that we've gone through all the messages, + //go back and try and link up the stray threads + if (count($strays)>0){ + foreach($strays as $id=>$irt){ + $root_id = $sub_mids[$irt]; + if (!$root_id || $root_id==$id) continue; + $result[$root_id] = array_merge($result[$root_id],$result[$id]); + unset($result[$id]); + } + } + + if ($clock) $clock->register('data prepped'); + + if ($debug) print_r($roots); + //print_r($result); + return $result; +} + + +function iil_SortThreads(&$tree, $index, $sort_order='ASC'){ + if (!is_array($tree) || !is_array($index)) return false; + + //create an id to position lookup table + $i = 0; + foreach($index as $id=>$val){ + $i++; + $index[$id] = $i; + } + $max = $i+1; + + //for each tree, set array key to position + $itree = array(); + foreach($tree as $id=>$node){ + if (count($tree[$id])<=1){ + //for "threads" with only one message, key is position of that message + $n = $index[$id]; + $itree[$n] = array($n=>$id); + }else{ + //for "threads" with multiple messages, + $min = $max; + $new_a = array(); + foreach($tree[$id] as $mid){ + $new_a[$index[$mid]] = $mid; //create new sub-array mapping position to id + $pos = $index[$mid]; + if ($pos&&$pos<$min) $min = $index[$mid]; //find smallest position + } + $n = $min; //smallest position of child is thread position + + //assign smallest position to root level key + //set children array to one created above + ksort($new_a); + $itree[$n] = $new_a; + } + } + + + //sort by key, this basically sorts all threads + ksort($itree); + $i=0; + $out=array(); + foreach($itree as $k=>$node){ + $out[$i] = $itree[$k]; + $i++; + } + + //return + return $out; +} + +function iil_IndexThreads(&$tree){ + /* creates array mapping mid to thread id */ + + if (!is_array($tree)) return false; + + $t_index = array(); + foreach($tree as $pos=>$kids){ + foreach($kids as $kid) $t_index[$kid] = $pos; + } + + return $t_index; +} + +function iil_C_FetchHeaders(&$conn, $mailbox, $message_set){ + global $IMAP_USE_INTERNAL_DATE; + + $c=0; + $result=array(); + $fp = $conn->fp; + + if (empty($message_set)) return array(); + + /* Do "SELECT" command */ + if (!iil_C_Select($conn, $mailbox)){ + $conn->error = "Couldn't select $mailbox"; + return false; + } + + /* Get cached records where possible */ + if ($conn->do_cache){ + $uids = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, "UID"); + if (is_array($uids) && count($conn->cache[$mailbox]>0)){ + $needed_set = ""; + while(list($id,$uid)=each($uids)){ + if ($conn->cache[$mailbox][$uid]){ + $result[$id] = $conn->cache[$mailbox][$uid]; + $result[$id]->id = $id; + }else $needed_set.=($needed_set?",":"").$id; + } + //echo "<!-- iil_C_FetchHeader\nMessage Set: $message_set\nNeeded Set:$needed_set\n//-->\n"; + if ($needed_set) $message_set = iil_CompressMessageSet($needed_set); + else return $result; + } + } + + /* FETCH date,from,subject headers */ + $key="fh".($c++); + $request=$key." FETCH $message_set (BODY.PEEK[HEADER.FIELDS (DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID)])\r\n"; + + // echo "// $request\n\n"; + + if (!fputs($fp, $request)) return false; + do{ + $line=chop(iil_ReadLine($fp, 200)); + $a=explode(" ", $line); + if (($line[0]=="*") && ($a[2]=="FETCH")){ + $id=$a[1]; + $result[$id]=new iilBasicHeader; + $result[$id]->id = $id; + $result[$id]->subject = ""; + /* + Start parsing headers. The problem is, some header "lines" take up multiple lines. + So, we'll read ahead, and if the one we're reading now is a valid header, we'll + process the previous line. Otherwise, we'll keep adding the strings until we come + to the next valid header line. + */ + $i = 0; + $lines = array(); + do{ + $line = chop(iil_ReadLine($fp, 300),"\r\n"); + if (ord($line[0])<=32) $lines[$i].=(empty($lines[$i])?"":"\n").trim(chop($line)); + else{ + $i++; + $lines[$i] = trim(chop($line)); + } + }while($line[0]!=")"); + + //process header, fill iilBasicHeader obj. + // initialize + if (is_array($headers)){ + reset($headers); + while ( list($k, $bar) = each($headers) ) $headers[$k] = ""; + } + + // create array with header field:data + $headers = array(); + while ( list($lines_key, $str) = each($lines) ){ + list($field, $string) = iil_SplitHeaderLine($str); + $field = strtolower($field); + $headers[$field] = $string; + } + $result[$id]->date = $headers["date"]; + $result[$id]->timestamp = iil_StrToTime($headers["date"]); + $result[$id]->from = $headers["from"]; + $result[$id]->to = str_replace("\n", " ", $headers["to"]); + $result[$id]->subject = str_replace("\n", "", $headers["subject"]); + $result[$id]->replyto = str_replace("\n", " ", $headers["reply-to"]); + $result[$id]->cc = str_replace("\n", " ", $headers["cc"]); + $result[$id]->encoding = str_replace("\n", " ", $headers["content-transfer-encoding"]); + $result[$id]->ctype = str_replace("\n", " ", $headers["content-type"]); + //$result[$id]->in_reply_to = ereg_replace("[\n<>]",'', $headers['in-reply-to']); + list($result[$id]->ctype,$foo) = explode(";", $headers["content-type"]); + $messageID = $headers["message-id"]; + if ($messageID) $messageID = substr(substr($messageID, 1), 0, strlen($messageID)-2); + else $messageID = "mid:".$id; + $result[$id]->messageID = $messageID; + + } + }while(strcmp($a[0], $key)!=0); + + /* + FETCH uid, size, flags + Sample reply line: "* 3 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen \Deleted))" + */ + $command_key="fh".($c++); + $request= $command_key." FETCH $message_set (UID RFC822.SIZE FLAGS INTERNALDATE)\r\n"; + if (!fputs($fp, $request)) return false; + do{ + $line=chop(iil_ReadLine($fp, 200)); + //$a = explode(" ", $line); + //if (($line[0]=="*") && ($a[2]=="FETCH")){ + if ($line[0]=="*"){ + //echo "<!-- $line //-->\n"; + //get outter most parens + $open_pos = strpos($line, "(") + 1; + $close_pos = strrpos($line, ")"); + if ($open_pos && $close_pos){ + //extract ID from pre-paren + $pre_str = substr($line, 0, $open_pos); + $pre_a = explode(" ", $line); + $id = $pre_a[1]; + + //get data + $len = $close_pos - $open_pos; + $str = substr($line, $open_pos, $len); + + //swap parents with quotes, then explode + $str = eregi_replace("[()]", "\"", $str); + $a = iil_ExplodeQuotedString(" ", $str); + + //did we get the right number of replies? + $parts_count = count($a); + if ($parts_count>=8){ + for ($i=0;$i<$parts_count;$i=$i+2){ + if (strcasecmp($a[$i],"UID")==0) $result[$id]->uid=$a[$i+1]; + else if (strcasecmp($a[$i],"RFC822.SIZE")==0) $result[$id]->size=$a[$i+1]; + else if (strcasecmp($a[$i],"INTERNALDATE")==0) $time_str = $a[$i+1]; + else if (strcasecmp($a[$i],"FLAGS")==0) $flags_str = $a[$i+1]; + } + + // process flags + $flags_str = eregi_replace('[\\\"]', "", $flags_str); + $flags_a = explode(" ", $flags_str); + //echo "<!-- ID: $id FLAGS: ".implode(",", $flags_a)." //-->\n"; + + $result[$id]->seen = false; + $result[$id]->recent = false; + $result[$id]->deleted = false; + $result[$id]->answered = false; + if (is_array($flags_a)){ + reset($flags_a); + while (list($key,$val)=each($flags_a)){ + if (strcasecmp($val,"Seen")==0) $result[$id]->seen = true; + else if (strcasecmp($val, "Deleted")==0) $result[$id]->deleted=true; + else if (strcasecmp($val, "Recent")==0) $result[$id]->recent = true; + else if (strcasecmp($val, "Answered")==0) $result[$id]->answered = true; + } + $result[$id]->flags=$flags_str; + } + + // if time is gmt... + $time_str = str_replace('GMT','+0000',$time_str); + + //get timezone + $time_str = substr($time_str, 0, -1); + $time_zone_str = substr($time_str, -5); //extract timezone + $time_str = substr($time_str, 1, -6); //remove quotes + $time_zone = (float)substr($time_zone_str, 1, 2); //get first two digits + if ($time_zone_str[3]!='0') $time_zone += 0.5; //handle half hour offset + if ($time_zone_str[0]=="-") $time_zone = $time_zone * -1.0; //minus? + $result[$id]->internaldate = $time_str; + + if ($IMAP_USE_INTERNAL_DATE){ + //calculate timestamp + $timestamp = strtotime($time_str); //return's server's time + $na_timestamp = $timestamp; + $timestamp -= $time_zone * 3600; //compensate for tz, get GMT + $result[$id]->timestamp = $timestamp; + } + + if ($conn->do_cache){ + $uid = $result[$id]->uid; + $conn->cache[$mailbox][$uid] = $result[$id]; + $conn->cache_dirty[$mailbox] = true; + } + //echo "<!-- ID: $id : $time_str -- local: $na_timestamp (".date("F j, Y, g:i a", $na_timestamp).") tz: $time_zone -- GMT: ".$timestamp." (".date("F j, Y, g:i a", $timestamp).") //-->\n"; + }else{ + //echo "<!-- ERROR: $id : $str //-->\n"; + } + } + } + }while(strpos($line, $command_key)===false); + + return $result; +} + + +function iil_C_FetchHeader(&$conn, $mailbox, $id){ + $fp = $conn->fp; + $a=iil_C_FetchHeaders($conn, $mailbox, $id); + if (is_array($a)) return $a[$id]; + else return false; +} + + +function iil_SortHeaders($a, $field, $flag){ + if (empty($field)) $field="uid"; + $field=strtolower($field); + if ($field=="date"||$field=='internaldate') $field="timestamp"; + if (empty($flag)) $flag="ASC"; + $flag=strtoupper($flag); + + $c=count($a); + if ($c>0){ + /* + Strategy: + First, we'll create an "index" array. + Then, we'll use sort() on that array, + and use that to sort the main array. + */ + + // create "index" array + $index=array(); + reset($a); + while (list($key, $val)=each($a)){ + $data=$a[$key]->$field; + if (is_string($data)) $data=strtoupper(str_replace("\"", "", $data)); + $index[$key]=$data; + } + + // sort index + $i=0; + if ($flag=="ASC") asort($index); + else arsort($index); + + // form new array based on index + $result=array(); + reset($index); + while (list($key, $val)=each($index)){ + $result[$i]=$a[$key]; + $i++; + } + } + + return $result; +} + +function iil_C_Expunge(&$conn, $mailbox){ + $fp = $conn->fp; + if (iil_C_Select($conn, $mailbox)){ + $c=0; + fputs($fp, "exp1 EXPUNGE\r\n"); + do{ + $line=chop(iil_ReadLine($fp, 100)); + if ($line[0]=="*") $c++; + }while (!iil_StartsWith($line, "exp1")); + + if (iil_ParseResult($line) == 0){ + $conn->selected = ""; //state has changed, need to reselect + //$conn->exists-=$c; + return $c; + }else{ + $conn->error = $line; + return -1; + } + } + + return -1; +} + +function iil_C_ModFlag(&$conn, $mailbox, $messages, $flag, $mod){ + if ($mod!="+" && $mod!="-") return -1; + + $fp = $conn->fp; + $flags=array( + "SEEN"=>"\\Seen", + "DELETED"=>"\\Deleted", + "RECENT"=>"\\Recent", + "ANSWERED"=>"\\Answered", + "DRAFT"=>"\\Draft", + "FLAGGED"=>"\\Flagged" + ); + $flag=strtoupper($flag); + $flag=$flags[$flag]; + if (iil_C_Select($conn, $mailbox)){ + $c=0; + fputs($fp, "flg STORE $messages ".$mod."FLAGS (".$flag.")\r\n"); + do{ + $line=chop(iil_ReadLine($fp, 100)); + if ($line[0]=="*") $c++; + }while (!iil_StartsWith($line, "flg")); + + if (iil_ParseResult($line) == 0){ + iil_C_ExpireCachedItems($conn, $mailbox, $messages); + return $c; + }else{ + $conn->error = $line; + return -1; + } + }else{ + $conn->error = "Select failed"; + return -1; + } +} + +function iil_C_Flag(&$conn, $mailbox, $messages, $flag){ + return iil_C_ModFlag($conn, $mailbox, $messages, $flag, "+"); +} + +function iil_C_Unflag(&$conn, $mailbox, $messages, $flag){ + return iil_C_ModFlag($conn, $mailbox, $messages, $flag, "-"); +} + +function iil_C_Delete(&$conn, $mailbox, $messages){ + return iil_C_ModFlag($conn, $mailbox, $messages, "DELETED", "+"); +} + +function iil_C_Undelete(&$conn, $mailbox, $messages){ + return iil_C_ModFlag($conn, $mailbox, $messages, "DELETED", "-"); +} + + +function iil_C_Unseen(&$conn, $mailbox, $messages){ + return iil_C_ModFlag($conn, $mailbox, $messages, "SEEN", "-"); +} + + +function iil_C_Copy(&$conn, $messages, $from, $to){ + $fp = $conn->fp; + + if (empty($from) || empty($to)) return -1; + + if (iil_C_Select($conn, $from)){ + $c=0; + + fputs($fp, "cpy1 COPY $messages \"$to\"\r\n"); + $line=iil_ReadReply($fp); + return iil_ParseResult($line); + }else{ + return -1; + } +} + +function iil_FormatSearchDate($month, $day, $year){ + $month = (int)$month; + $months=array( + 1=>"Jan", 2=>"Feb", 3=>"Mar", 4=>"Apr", + 5=>"May", 6=>"Jun", 7=>"Jul", 8=>"Aug", + 9=>"Sep", 10=>"Oct", 11=>"Nov", 12=>"Dec" + ); + return $day."-".$months[$month]."-".$year; +} + +function iil_C_CountUnseen(&$conn, $folder){ + $index = iil_C_Search($conn, $folder, "ALL UNSEEN"); + if (is_array($index)){ + $str = implode(",", $index); + if (empty($str)) return false; + else return count($index); + }else return false; +} + +function iil_C_UID2ID(&$conn, $folder, $uid){ + if ($uid > 0){ + $id_a = iil_C_Search($conn, $folder, "UID $uid"); + if (is_array($id_a)){ + $count = count($id_a); + if ($count > 1) return false; + else return $id_a[0]; + } + } + return false; +} + +function iil_C_Search(&$conn, $folder, $criteria){ + $fp = $conn->fp; + if (iil_C_Select($conn, $folder)){ + $c=0; + + $query = "srch1 SEARCH ".chop($criteria)."\r\n"; + fputs($fp, $query); + do{ + $line=trim(chop(iil_ReadLine($fp, 10000))); + if (eregi("^\* SEARCH", $line)){ + $str = trim(substr($line, 8)); + $messages = explode(" ", $str); + } + }while(!iil_StartsWith($line, "srch1")); + + $result_code=iil_ParseResult($line); + if ($result_code==0) return $messages; + else{ + $conn->error = "iil_C_Search: ".$line."<br>\n"; + return false; + } + + }else{ + $conn->error = "iil_C_Search: Couldn't select \"$folder\" <br>\n"; + return false; + } +} + +function iil_C_Move(&$conn, $messages, $from, $to){ + $fp = $conn->fp; + + if (!$from || !$to) return -1; + + $r=iil_C_Copy($conn, $messages, $from,$to); + if ($r==0){ + return iil_C_Delete($conn, $from, $messages); + }else{ + return $r; + } +} + +function iil_C_GetHierarchyDelimiter(&$conn){ + if ($conn->delimiter) return $conn->delimiter; + + $fp = $conn->fp; + $delimiter = false; + + //try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8) + if (!fputs($fp, "ghd LIST \"\" \"\"\r\n")) return false; + do{ + $line=iil_ReadLine($fp, 500); + if ($line[0]=="*"){ + $line = rtrim($line); + $a=iil_ExplodeQuotedString(" ", $line); + if ($a[0]=="*") $delimiter = str_replace("\"", "", $a[count($a)-2]); + } + }while (!iil_StartsWith($line, "ghd")); + + if (strlen($delimiter)>0) return $delimiter; + + //if that fails, try namespace extension + //try to fetch namespace data + fputs($conn->fp, "ns1 NAMESPACE\r\n"); + do{ + $line = iil_ReadLine($conn->fp, 1024); + if (iil_StartsWith($line, "* NAMESPACE")){ + $i = 0; + $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0); + } + }while(!iil_StartsWith($line, "ns1")); + + if (!is_array($data)) return false; + + //extract user space data (opposed to global/shared space) + $user_space_data = $data[0]; + if (!is_array($user_space_data)) return false; + + //get first element + $first_userspace = $user_space_data[0]; + if (!is_array($first_userspace)) return false; + + //extract delimiter + $delimiter = $first_userspace[1]; + + return $delimiter; +} + +function iil_C_ListMailboxes(&$conn, $ref, $mailbox){ + global $IGNORE_FOLDERS; + + $ignore = $IGNORE_FOLDERS[strtolower($conn->host)]; + + $fp = $conn->fp; + if (empty($mailbox)) $mailbox="*"; + if (empty($ref) && $conn->rootdir) $ref = $conn->rootdir; + + // send command + if (!fputs($fp, "lmb LIST \"".$ref."\" \"$mailbox\"\r\n")) return false; + $i=0; + // get folder list + do{ + $line=iil_ReadLine($fp, 500); + $line=iil_MultLine($fp, $line); + + $a = explode(" ", $line); + if (($line[0]=="*") && ($a[1]=="LIST")){ + $line = rtrim($line); + // split one line + $a=iil_ExplodeQuotedString(" ", $line); + // last string is folder name + $folder = str_replace("\"", "", $a[count($a)-1]); + if (empty($ignore) || (!empty($ignore) && !eregi($ignore, $folder))) $folders[$i] = $folder; + // second from last is delimiter + $delim = str_replace("\"", "", $a[count($a)-2]); + // is it a container? + $i++; + } + }while (!iil_StartsWith($line, "lmb")); + + if (is_array($folders)){ + if (!empty($ref)){ + // if rootdir was specified, make sure it's the first element + // some IMAP servers (i.e. Courier) won't return it + if ($ref[strlen($ref)-1]==$delim) $ref = substr($ref, 0, strlen($ref)-1); + if ($folders[0]!=$ref) array_unshift($folders, $ref); + } + return $folders; + }else if (iil_ParseResult($line)==0){ + return array('INBOX'); + }else{ + $conn->error = $line; + return false; + } +} + + +function iil_C_ListSubscribed(&$conn, $ref, $mailbox){ + global $IGNORE_FOLDERS; + + $ignore = $IGNORE_FOLDERS[strtolower($conn->host)]; + + $fp = $conn->fp; + if (empty($mailbox)) $mailbox = "*"; + if (empty($ref) && $conn->rootdir) $ref = $conn->rootdir; + $folders = array(); + + // send command + if (!fputs($fp, "lsb LSUB \"".$ref."\" \"".$mailbox."\"\r\n")){ + $conn->error = "Couldn't send LSUB command\n"; + return false; + } + $i=0; + // get folder list + do{ + $line=iil_ReadLine($fp, 500); + $line=iil_MultLine($fp, $line); + $a = explode(" ", $line); + if (($line[0]=="*") && ($a[1]=="LSUB")){ + $line = rtrim($line); + // split one line + $a=iil_ExplodeQuotedString(" ", $line); + // last string is folder name + //$folder = UTF7DecodeString(str_replace("\"", "", $a[count($a)-1])); + $folder = str_replace("\"", "", $a[count($a)-1]); + if ((!in_array($folder, $folders)) && (empty($ignore) || (!empty($ignore) && !eregi($ignore, $folder)))) $folders[$i] = $folder; + // second from last is delimiter + $delim = str_replace("\"", "", $a[count($a)-2]); + // is it a container? + $i++; + } + }while (!iil_StartsWith($line, "lsb")); + + if (is_array($folders)){ + if (!empty($ref)){ + // if rootdir was specified, make sure it's the first element + // some IMAP servers (i.e. Courier) won't return it + if ($ref[strlen($ref)-1]==$delim) $ref = substr($ref, 0, strlen($ref)-1); + if ($folders[0]!=$ref) array_unshift($folders, $ref); + } + return $folders; + }else{ + $conn->error = $line; + return false; + } +} + + +function iil_C_Subscribe(&$conn, $folder){ + $fp = $conn->fp; + + $query = "sub1 SUBSCRIBE \"".$folder."\"\r\n"; + fputs($fp, $query); + $line=trim(chop(iil_ReadLine($fp, 10000))); + return iil_ParseResult($line); +} + + +function iil_C_UnSubscribe(&$conn, $folder){ + $fp = $conn->fp; + + $query = "usub1 UNSUBSCRIBE \"".$folder."\"\r\n"; + fputs($fp, $query); + $line=trim(chop(iil_ReadLine($fp, 10000))); + return iil_ParseResult($line); +} + + +function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part){ + $fp = $conn->fp; + $result=false; + if (($part==0)||(empty($part))) $part="HEADER"; + else $part.=".MIME"; + + if (iil_C_Select($conn, $mailbox)){ + $key="fh".($c++); + $request=$key." FETCH $id (BODY.PEEK[$part])\r\n"; + if (!fputs($fp, $request)) return false; + do{ + $line=chop(iil_ReadLine($fp, 200)); + $a=explode(" ", $line); + if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[strlen($line)-1]!=")")){ + $line=iil_ReadLine($fp, 300); + while(chop($line)!=")"){ + $result.=$line; + $line=iil_ReadLine($fp, 300); + } + } + }while(strcmp($a[0], $key)!=0); + } + + return $result; +} + + +function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part, $mode){ + /* modes: + 1: return string + 2: print + 3: base64 and print + */ + $fp = $conn->fp; + $result=false; + if (($part==0)||(empty($part))) $part="TEXT"; + + if (iil_C_Select($conn, $mailbox)){ + $reply_key="* ".$id; + // format request + $key="ftch".($c++)." "; + $request=$key."FETCH $id (BODY.PEEK[$part])\r\n"; + // send request + if (!fputs($fp, $request)) return false; + // receive reply line + do{ + $line = chop(iil_ReadLine($fp, 1000)); + $a = explode(" ", $line); + }while ($a[2]!="FETCH"); + $len = strlen($line); + if ($line[$len-1] == ")"){ + //one line response, get everything between first and last quotes + $from = strpos($line, "\"") + 1; + $to = strrpos($line, "\""); + $len = $to - $from; + if ($mode==1) $result = substr($line, $from, $len); + else if ($mode==2) echo substr($line, $from, $len); + else if ($mode==3) echo base64_decode(substr($line, $from, $len)); + }else if ($line[$len-1] == "}"){ + //multi-line request, find sizes of content and receive that many bytes + $from = strpos($line, "{") + 1; + $to = strrpos($line, "}"); + $len = $to - $from; + $sizeStr = substr($line, $from, $len); + $bytes = (int)$sizeStr; + $received = 0; + while ($received < $bytes){ + $remaining = $bytes - $received; + $line = iil_ReadLine($fp, 1024); + $len = strlen($line); + if ($len > $remaining) substr($line, 0, $remaining); + $received += strlen($line); + if ($mode==1) $result .= chop($line)."\n"; + else if ($mode==2){ echo chop($line)."\n"; flush(); } + else if ($mode==3){ echo base64_decode($line); flush(); } + } + } + // read in anything up until 'til last line + do{ + $line = iil_ReadLine($fp, 1024); + }while(!iil_StartsWith($line, $key)); + + if ($result){ + $result = chop($result); + return substr($result, 0, strlen($result)-1); + }else return false; + }else{ + echo "Select failed."; + } + + if ($mode==1) return $result; + else return $received; +} + +function iil_C_FetchPartBody(&$conn, $mailbox, $id, $part){ + return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1); +} + +function iil_C_PrintPartBody(&$conn, $mailbox, $id, $part){ + iil_C_HandlePartBody($conn, $mailbox, $id, $part, 2); +} + +function iil_C_PrintBase64Body(&$conn, $mailbox, $id, $part){ + iil_C_HandlePartBody($conn, $mailbox, $id, $part, 3); +} + +function iil_C_CreateFolder(&$conn, $folder){ + $fp = $conn->fp; + if (fputs($fp, "c CREATE \"".$folder."\"\r\n")){ + do{ + $line=iil_ReadLine($fp, 300); + }while($line[0]!="c"); + $conn->error = $line; + return (iil_ParseResult($line)==0); + }else{ + return false; + } +} + +function iil_C_RenameFolder(&$conn, $from, $to){ + $fp = $conn->fp; + if (fputs($fp, "r RENAME \"".$from."\" \"".$to."\"\r\n")){ + do{ + $line=iil_ReadLine($fp, 300); + }while($line[0]!="r"); + return (iil_ParseResult($line)==0); + }else{ + return false; + } +} + +function iil_C_DeleteFolder(&$conn, $folder){ + $fp = $conn->fp; + if (fputs($fp, "d DELETE \"".$folder."\"\r\n")){ + do{ + $line=iil_ReadLine($fp, 300); + }while($line[0]!="d"); + return (iil_ParseResult($line)==0); + }else{ + $conn->error = "Couldn't send command\n"; + return false; + } +} + +function iil_C_Append(&$conn, $folder, $message){ + if (!$folder) return false; + $fp = $conn->fp; + + $message = str_replace("\r", "", $message); + $message = str_replace("\n", "\r\n", $message); + + $len = strlen($message); + if (!$len) return false; + + $request="A APPEND \"".$folder."\" (\\Seen) {".$len."}\r\n"; + // echo $request.'<br>'; + if (fputs($fp, $request)){ + $line=iil_ReadLine($fp, 100); + // echo $line.'<br>'; + + $sent = fwrite($fp, $message."\r\n"); + flush(); + do{ + $line=iil_ReadLine($fp, 1000); + //echo $line.'<br>'; + }while($line[0]!="A"); + + $result = (iil_ParseResult($line)==0); + if (!$result) $conn->error .= $line."<br>\n"; + return $result; + + }else{ + $conn->error .= "Couldn't send command \"$request\"<br>\n"; + return false; + } +} + + +function iil_C_AppendFromFile(&$conn, $folder, $path){ + if (!$folder) return false; + + //open message file + $in_fp = false; + if (file_exists(realpath($path))) $in_fp = fopen($path, "r"); + if (!$in_fp){ + $conn->error .= "Couldn't open $path for reading<br>\n"; + return false; + } + + $fp = $conn->fp; + $len = filesize($path); + if (!$len) return false; + + //send APPEND command + $request="A APPEND \"".$folder."\" (\\Seen) {".$len."}\r\n"; + $bytes_sent = 0; + if (fputs($fp, $request)){ + $line=iil_ReadLine($fp, 100); + + //send file + while(!feof($in_fp)){ + $buffer = fgets($in_fp, 4096); + $bytes_sent += strlen($buffer); + fputs($fp, $buffer); + } + fclose($in_fp); + + fputs($fp, "\r\n"); + + //read response + do{ + $line=iil_ReadLine($fp, 1000); + //echo $line.'<br>'; + }while($line[0]!="A"); + + $result = (iil_ParseResult($line)==0); + if (!$result) $conn->error .= $line."<br>\n"; + return $result; + + }else{ + $conn->error .= "Couldn't send command \"$request\"<br>\n"; + return false; + } +} + + +function iil_C_FetchStructureString(&$conn, $folder, $id){ + $fp = $conn->fp; + $result=false; + if (iil_C_Select($conn, $folder)){ + $key = "F1247"; + if (fputs($fp, "$key FETCH $id (BODYSTRUCTURE)\r\n")){ + do{ + $line=chop(iil_ReadLine($fp, 5000)); + if ($line[0]=="*"){ + if (ereg("\}$", $line)){ + preg_match('/(.+)\{([0-9]+)\}/', $line, $match); + $result = $match[1]; + do{ + $line = chop(iil_ReadLine($fp, 100)); + if (!preg_match("/^$key/", $line)) $result .= $line; + else $done = true; + }while(!$done); + }else{ + $result = $line; + } + list($pre, $post) = explode("BODYSTRUCTURE ", $result); + $result = substr($post, 0, strlen($post)-1); //truncate last ')' and return + } + }while (!preg_match("/^$key/",$line)); + } + } + return $result; +} + +function iil_C_PrintSource(&$conn, $folder, $id, $part){ + $header = iil_C_FetchPartHeader($conn, $folder, $id, $part); + //echo str_replace("\r", "", $header); + echo $header; + echo iil_C_PrintPartBody($conn, $folder, $id, $part); +} + +function iil_C_GetQuota(&$conn){ +/* +b GETQUOTAROOT "INBOX" +* QUOTAROOT INBOX user/rchijiiwa1 +* QUOTA user/rchijiiwa1 (STORAGE 654 9765) +b OK Completed +*/ + $fp = $conn->fp; + $result=false; + $quota_line = ""; + + //get line containing quota info + if (fputs($fp, "QUOT1 GETQUOTAROOT \"INBOX\"\r\n")){ + do{ + $line=chop(iil_ReadLine($fp, 5000)); + if (iil_StartsWith($line, "* QUOTA ")) $quota_line = $line; + }while(!iil_StartsWith($line, "QUOT1")); + } + + //return false if not found, parse if found + if (!empty($quota_line)){ + $quota_line = eregi_replace("[()]", "", $quota_line); + $parts = explode(" ", $quota_line); + $storage_part = array_search("STORAGE", $parts); + if ($storage_part>0){ + $result = array(); + $used = $parts[$storage_part+1]; + $total = $parts[$storage_part+2]; + $result["used"] = $used; + $result["total"] = (empty($total)?"??":$total); + $result["percent"] = (empty($total)?"??":round(($used/$total)*100)); + $result["free"] = 100 - $result["percent"]; + } + } + + return $result; +} + + +function iil_C_ClearFolder(&$conn, $folder){ + $num_in_trash = iil_C_CountMessages($conn, $folder); + if ($num_in_trash > 0) iil_C_Delete($conn, $folder, "1:".$num_in_trash); + return (iil_C_Expunge($conn, $folder) >= 0); +} + +?>
\ No newline at end of file diff --git a/program/lib/mime.inc b/program/lib/mime.inc new file mode 100644 index 000000000..ad6561ed7 --- /dev/null +++ b/program/lib/mime.inc @@ -0,0 +1,322 @@ +<?php +///////////////////////////////////////////////////////// +// +// Iloha MIME Library (IML) +// +// (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org> +// +// This file is part of IlohaMail. IlohaMail is free software released +// under the GPL license. See enclosed file COPYING for details, or +// see http://www.fsf.org/copyleft/gpl.html +// +///////////////////////////////////////////////////////// + +/******************************************************** + + FILE: include/mime.inc + PURPOSE: + Provide functions for handling mime messages. + USAGE: + Use iil_C_FetchStructureString to get IMAP structure stirng, then pass that through + iml_GetRawStructureArray() to get root node to a nested data structure. + Pass root node to the iml_GetPart*() functions to retreive individual bits of info. + +********************************************************/ +$MIME_INVALID = -1; +$MIME_TEXT = 0; +$MIME_MULTIPART = 1; +$MIME_MESSAGE = 2; +$MIME_APPLICATION = 3; +$MIME_AUDIO = 4; +$MIME_IMAGE = 5; +$MIME_VIDEO = 6; +$MIME_OTHER = 7; + +function iml_ClosingParenPos($str, $start){ + $level=0; + $len = strlen($str); + $in_quote = 0; + for ($i=$start;$i<$len;$i++){ + if ($str[$i]=="\"") $in_quote = ($in_quote + 1) % 2; + if (!$in_quote){ + if ($str[$i]=="(") $level++; + else if (($level > 0) && ($str[$i]==")")) $level--; + else if (($level == 0) && ($str[$i]==")")) return $i; + } + } +} + +function iml_ParseBSString($str){ + + $id = 0; + $a = array(); + $len = strlen($str); + + $in_quote = 0; + for ($i=0; $i<$len; $i++){ + if ($str[$i] == "\"") $in_quote = ($in_quote + 1) % 2; + else if (!$in_quote){ + if ($str[$i] == " ") $id++; //space means new element + else if ($str[$i]=="("){ //new part + $i++; + $endPos = iml_ClosingParenPos($str, $i); + $partLen = $endPos - $i; + $part = substr($str, $i, $partLen); + $a[$id] = iml_ParseBSString($part); //send part string + if ($verbose){ + echo "{>".$endPos."}"; + flush(); + } + $i = $endPos; + }else $a[$id].=$str[$i]; //add to current element in array + }else if ($in_quote){ + if ($str[$i]=="\\") $i++; //escape backslashes + else $a[$id].=$str[$i]; //add to current element in array + } + } + + reset($a); + return $a; +} + +function iml_GetRawStructureArray($str){ + $line=substr($str, 1, strlen($str) - 2); + $line = str_replace(")(", ") (", $line); + + $struct = iml_ParseBSString($line); + if ((strcasecmp($struct[0], "message")==0) && (strcasecmp($struct[1], "rfc822")==0)){ + $struct = array($struct); + } + return $struct; +} + +function iml_GetPartArray($a, $part){ + if (!is_array($a)) return false; + if (strpos($part, ".") > 0){ + $original_part = $part; + $pos = strpos($part, "."); + $rest = substr($original_part, $pos+1); + $part = substr($original_part, 0, $pos); + if ((strcasecmp($a[0], "message")==0) && (strcasecmp($a[1], "rfc822")==0)){ + $a = $a[8]; + } + //echo "m - part: $original_part current: $part rest: $rest array: ".implode(" ", $a)."<br>\n"; + return iml_GetPartArray($a[$part-1], $rest); + }else if ($part>0){ + if ((strcasecmp($a[0], "message")==0) && (strcasecmp($a[1], "rfc822")==0)){ + $a = $a[8]; + } + //echo "s - part: $part rest: $rest array: ".implode(" ", $a)."<br>\n"; + if (is_array($a[$part-1])) return $a[$part-1]; + else return false; + }else if (($part==0) || (empty($part))){ + return $a; + } +} + +function iml_GetNumParts($a, $part){ + if (is_array($a)){ + $parent=iml_GetPartArray($a, $part); + + if ((strcasecmp($parent[0], "message")==0) && (strcasecmp($parent[1], "rfc822")==0)){ + $parent = $parent[8]; + } + + $is_array=true; + $c=0; + while (( list ($key, $val) = each ($parent) )&&($is_array)){ + $is_array=is_array($parent[$key]); + if ($is_array) $c++; + } + return $c; + } + + return false; +} + +function iml_GetPartTypeString($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])){ + $type_str = "MULTIPART/"; + reset($part_a); + while(list($n,$element)=each($part_a)){ + if (!is_array($part_a[$n])){ + $type_str.=$part_a[$n]; + break; + } + } + return $type_str; + }else return $part_a[0]."/".$part_a[1]; + }else return false; +} + +function iml_GetFirstTextPart($structure,$part){ + if ($part==0) $part=""; + $typeCode = -1; + while ($typeCode!=0){ + $typeCode = iml_GetPartTypeCode($structure, $part); + if ($typeCode == 1){ + $part .= (empty($part)?"":".")."1"; + }else if ($typeCode > 0){ + $parts_a = explode(".", $part); + $lastPart = count($parts_a) - 1; + $parts_a[$lastPart] = (int)$parts_a[$lastPart] + 1; + $part = implode(".", $parts_a); + }else if ($typeCode == -1){ + return ""; + } + } + + return $part; +} + +function iml_GetPartTypeCode($a, $part){ + $types=array(0=>"text",1=>"multipart",2=>"message",3=>"application",4=>"audio",5=>"image",6=>"video",7=>"other"); + + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) $str="multipart"; + else $str=$part_a[0]; + + $code=7; + while ( list($key, $val) = each($types)) if (strcasecmp($val, $str)==0) $code=$key; + return $code; + }else return -1; +} + +function iml_GetPartEncodingCode($a, $part){ + $encodings=array("7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE", "OTHER"); + + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else $str=$part_a[5]; + + $code=5; + while ( list($key, $val) = each($encodings)) if (strcasecmp($val, $str)==0) $code=$key; + + return $code; + + }else return -1; +} + +function iml_GetPartEncodingString($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else return $part_a[5]; + }else return -1; +} + +function iml_GetPartSize($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else return $part_a[6]; + }else return -1; +} + +function iml_GetPartID($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else return $part_a[3]; + }else return -1; +} + +function iml_GetPartDisposition($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else{ + $id = count($part_a) - 2; + if (is_array($part_a[$id])) return $part_a[$id][0]; + else return ""; + } + }else return ""; +} + +function iml_GetPartName($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else{ + $name = ""; + if (is_array($part_a[2])){ + //first look in content type + $name=""; + while ( list($key, $val) = each ($part_a[2])){ + if ((strcasecmp($val, "NAME")==0)||(strcasecmp($val, "FILENAME")==0)) + $name=$part_a[2][$key+1]; + } + } + if (empty($name)){ + //check in content disposition + $id = count($part_a) - 2; + if ((is_array($part_a[$id])) && (is_array($part_a[$id][1]))){ + $array = $part_a[$id][1]; + while ( list($key, $val) = each($array)){ + if ((strcasecmp($val, "NAME")==0)||(strcasecmp($val, "FILENAME")==0)) + $name=$array[$key+1]; + } + } + } + return $name; + } + }else return ""; +} + + +function iml_GetPartCharset($a, $part){ + $part_a=iml_GetPartArray($a, $part); + if ($part_a){ + if (is_array($part_a[0])) return -1; + else{ + if (is_array($part_a[2])){ + $name=""; + while ( list($key, $val) = each ($part_a[2])) if (strcasecmp($val, "charset")==0) $name=$part_a[2][$key+1]; + return $name; + } + else return ""; + } + }else return ""; +} + +function iml_GetPartList($a, $part){ + //echo "MOO?"; flush(); + $data = array(); + $num_parts = iml_GetNumParts($a, $part); + //echo "($num_parts)"; flush(); + if ($num_parts !== false){ + //echo "<!-- ($num_parts parts)//-->\n"; + for ($i = 0; $i<$num_parts; $i++){ + $part_code = $part.(empty($part)?"":".").($i+1); + $part_type = iml_GetPartTypeCode($a, $part_code); + $part_disposition = iml_GetPartDisposition($a, $part_code); + //echo "<!-- part: $part_code type: $part_type //-->\n"; + if (strcasecmp($part_disposition, "attachment")!=0 && + (($part_type == 1) || ($part_type==2))){ + $data = array_merge($data, iml_GetPartList($a, $part_code)); + }else{ + $data[$part_code]["typestring"] = iml_GetPartTypeString($a, $part_code); + $data[$part_code]["disposition"] = $part_disposition; + $data[$part_code]["size"] = iml_GetPartSize($a, $part_code); + $data[$part_code]["name"] = iml_GetPartName($a, $part_code); + $data[$part_code]["id"] = iml_GetPartID($a, $part_code); + } + } + } + return $data; +} + +function iml_GetNextPart($part){ + if (strpos($part, ".")===false) return $part++; + else{ + $parts_a = explode(".", $part); + $num_levels = count($parts_a); + $parts_a[$num_levels-1]++; + return implode(".", $parts_a); + } +} +?>
\ No newline at end of file diff --git a/program/lib/smtp.inc b/program/lib/smtp.inc new file mode 100644 index 000000000..ebff9d263 --- /dev/null +++ b/program/lib/smtp.inc @@ -0,0 +1,351 @@ +<?php +///////////////////////////////////////////////////////// +// +// include/smtp.inc +// +// (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org> +// +// This file is part of IlohaMail. +// IlohaMail is free software released under the GPL +// license. See enclosed file COPYING for details, +// or see http://www.fsf.org/copyleft/gpl.html +// +///////////////////////////////////////////////////////// + +/******************************************************** + + AUTHOR: Ryo Chijiiwa <ryo@ilohamail.org> + FILE: include/smtp.php + PURPOSE: + Provide SMTP functionality using pure PHP. + PRE-CONDITIONS: + The functions here require a SMTP server configured to allow relaying. + POST-CONDITIONS: + The following global variables are returned: + $smtp_errornum - error number, 0 if successful + $smtp_error - error message(s) + $SMTP_TYPE - Optional + COMMENTS: + The optional $smtp_message_file can be used for sending extremely large + messages. Storing large messages in a variable will consume whatever + amount of memory required, which may be more than is available if dealing + with messages with large attachments. By storing them in a file, it becomes + possible to read/send bits at a time, drastically reducing memory useage. + + This library only provides bare-bones SMTP functionality. It is up to the + parent code to form valid RFC822 (MIME) messages. + +********************************************************/ + + //set some global constants + if (strcasecmp($SMTP_TYPE, "courier")==0){ + $SMTP_REPLY_DELIM = "-"; + $SMTP_DATA_REPLY = 250; + }else{ + $SMTP_REPLY_DELIM = " "; + $SMTP_DATA_REPLY = 354; + } + + /* fgets replacement that's multi-line aware */ + function smtp_get_response($fp, $len){ + $end = false; + do{ + $line = chop(fgets($fp, 5120)); + // echo "SMTP:".$line."<br>\n"; flush(); + if ((strlen($line)==3) || ($line[3]==' ')) $end = true; + }while(!$end); + + return $line; + } + + + function smtp_check_reply($reply){ + global $smtp_error; + global $SMTP_REPLY_DELIM; + + $a = explode($SMTP_REPLY_DELIM, chop($reply)); + + if (count($a) >= 1){ + + if ($a[0]==250||$a[0]==354) return true; + else{ + $smtp_error .= $reply."\n"; + } + }else{ + $smtp_error .= "Invalid SMTP response line: $reply\n"; + } + + return false; + } + + + function smtp_split($str){ + $result = array(); + $pos = strpos($str, " "); + if ($pos===false){ + $result[0] = $str; + }else{ + $result[0] = substr($str, 0, $pos); + $result[1] = substr($str, $pos+1); + } + + return $result; + } + + + function smtp_ehlo(&$conn, $host){ + $result = ""; + fputs($conn, "EHLO $host\r\n"); + //echo "Sent: EHLO $host\n"; flush(); + do{ + $line = fgets($conn, 2048); + //echo "Got: $line"; flush(); + $a = explode(" ", $line); + if ($a[0]=="250-AUTH") $result .= substr($line, 9); + }while(!is_numeric($a[0])); + + if ($a[0]==250) return $result; + else return $a[0]; + + } + + + function smtp_auth_login(&$conn, $user, $pass){ + $auth["username"] = base64_encode($user); + $auth["password"] = base64_encode($pass); + + fputs($conn, "AUTH LOGIN\r\n"); + + //echo "Sent: AUTH LOGIN\n"; flush(); + + //get first line + $line = smtp_get_response($conn, 1024); + //echo "AUTH_LOGIN << $line"; flush(); + $parts = smtp_split($line); + //valid reply? + if (($parts[0]!=334) || (empty($parts[1]))) return false; + //send data + $prompt = eregi_replace("[^a-z]", "", strtolower(base64_decode($parts[1]))); + fputs($conn, $auth[$prompt]."\r\n"); + //echo "AUT_LOGIN >> ".$auth[$prompt]."\n"; flush(); + + //get second line + $line = smtp_get_response($conn, 1024); + //echo "AUTH_LOGIN << $line"; flush(); + $parts = smtp_split($line); + //valid reply? + if (($parts[0]!=334) || (empty($parts[1]))) return false; + $prompt = eregi_replace("[^a-z]", "", strtolower(base64_decode($parts[1]))); + fputs($conn, $auth[$prompt]."\r\n"); + //echo "AUT_LOGIN >> ".$auth[$prompt]."\n"; flush(); + + $line = smtp_get_response($conn, 1024); + //echo "AUTH_LOGIN << $line"; flush(); + $parts = smtp_split($line); + return ($parts[0]==235); + } + + + function smtp_connect($host, $port, $user, $pass){ + global $smtp_errornum; + global $smtp_error; + + //auth user? + global $SMTP_USER, $SMTP_PASSWORD; + if ((!empty($SMTP_USER)) && (!empty($SMTP_PASSWORD))){ + $user = $SMTP_USER; + $pass = $SMTP_PASSWORD; + } + + // echo "User: $user<br>\n"; + + //figure out auth mode + global $AUTH_MODE; + $auth_mode = $AUTH_MODE["smtp"]; + if (empty($auth_mode)) $auth_mode = "none"; + if (empty($user) || empty($pass)) $auth_mode = "none"; + + // echo "authmode: $auth_mode<br>\n"; flush(); + + //initialize defaults + if (empty($host)) $host = "localhost"; + if (empty($port)) $port = 25; + + // echo "Connecting to $host:$port<br>\n"; flush(); + + //connect to SMTP server + $conn = fsockopen($host, $port); + + if (!$conn){ + //echo "fsockopen failed\n"; + $smtp_error = "Couldn't connect to $host:$port<br>\n"; + return false; + } + + //read greeting + $greeting = smtp_get_response($conn, 1024); + + // echo "Connected: $greeting<br>\n"; flush(); + + if (($auth_mode=="check") || ($auth_mode=="auth")){ + // echo "Trying EHLO<br>\n"; flush(); + $auth_modes = smtp_ehlo($conn, $_SERVER["SERVER_NAME"]); + // echo "smtp_ehlo returned: $auth_modes<br>\n"; flush(); + if ($auth_modes===false){ + $smtp_error = "EHLO failed\n"; + $conn = false; + }else if (stristr($auth_modes, "LOGIN")!==false){ + echo "trying AUTH LOGIN\n"; flush(); + if (!smtp_auth_login($conn, $user, $pass)){ + //echo "CONN: AUTH_LOGIN failed\n"; flush(); + $conn = false; + } + //echo "Conn after LOGIN: $conn<br>\n"; flush(); + } + }else{ + fputs($conn, "HELO ".$_SERVER["SERVER_NAME"]."\r\n"); + $line = smtp_get_response($conn, 1024); + if (!smtp_check_reply($line)){ + $conn = false; + $smtp_error .= $line."\n"; + } + // echo "after HELO: $conn<br>\n"; flush(); + } + + return $conn; + } + + + function smtp_close($conn){ + fclose($conn); + } + + + function smtp_mail($conn, $from, $recipients, $message, $is_file){ + global $smtp_errornum; + global $smtp_error; + global $SMTP_DATA_REPLY; + + //check recipients and sender addresses + if ((count($recipients)==0) || (!is_array($recipients))){ + $smtp_errornum = -1; + $smtp_error .= "Recipients list is empty\n"; + return false; + } + if (empty($from)){ + $smtp_errornum = -2; + $smtp_error .= "From address unspecified\n"; + return false; + } + + if (!$conn){ + $smtp_errornum = -3; + $smtp_error .= "Invalid connection\n"; + } + + if (!ereg("^<", $from)) $from = "<".$from; + if (!ereg(">$", $from)) $from = $from.">"; + + //send MAIL FROM command + $command = "MAIL FROM: $from\r\n"; + // echo nl2br(htmlspecialchars($command)); + fputs($conn, $command); + + if (smtp_check_reply(smtp_get_response($conn, 1024))){ + //send RCPT TO commands, count valid recipients + $num_recipients = 0; + while ( list($k, $recipient) = each($recipients) ){ + $command = "RCPT TO: $recipient\r\n"; + fputs($conn, $command); + $reply = smtp_check_reply(smtp_get_response($conn, 1024)); + if ($reply) $num_recipients++; + else $smtp_error .= $reply."\n"; + } + + //error out if no valid recipiets + if ($num_recipients == 0){ + $smtp_errornum = -1; + $smtp_error .= "No valid recipients\n"; + return false; + } + + //send DATA command + fputs($conn, "DATA\r\n"); + $reply = chop(smtp_get_response($conn, 1024)); + $a = explode(" ", $reply); + + //error out if DATA command ill received + if ($a[0]!=$SMTP_DATA_REPLY){ + $smtp_errornum = -4; + $smtp_error .= $reply; + return false; + } + //send data + if ($is_file){ + //if message file, open file + $fp = false; + if (file_exists(realpath($message))) $fp = fopen($message, "rb"); + if (!$fp) + { + $smtp_errornum = -4; + $smtp_error .= "Invlid message file\n"; + return false; + } + + //send file + while(!feof($fp)){ + $buffer = chop(fgets($fp, 4096), "\r\n"); + fputs($conn, $buffer."\r\n"); + } + fclose($fp); + fputs($conn, "\r\n.\r\n"); + + return smtp_check_reply(smtp_get_response($conn, 1024)); + }else{ + //else, send message + $message = str_replace("\r\n", "\n", $message); + $message = str_replace("\n", "\r\n", $message); + $message = str_replace("\r\n.\r\n", "\r\n..\r\n", $message); + fputs($conn, $message); + fputs($conn, "\r\n.\r\n"); + + return smtp_check_reply(smtp_get_response($conn, 1024)); + } + } + + return false; + } + + + function smtp_ExplodeQuotedString($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; + } + + + function smtp_expand($str){ + $addresses = array(); + $recipients = smtp_ExplodeQuotedString(",", $str); + reset($recipients); + while ( list($k, $recipient) = each($recipients) ){ + $a = explode(" ", $recipient); + while ( list($k2, $word) = each($a) ){ + if ((strpos($word, "@") > 0) && (strpos($word, "\"")===false)){ + if (!ereg("^<", $word)) $word = "<".$word; + if (!ereg(">$", $word)) $word = $word.">"; + if (in_array($word, $addresses)===false) array_push($addresses, $word); + } + } + } + return $addresses; + } +?>
\ No newline at end of file diff --git a/program/lib/utf7.inc b/program/lib/utf7.inc new file mode 100644 index 000000000..a887958cf --- /dev/null +++ b/program/lib/utf7.inc @@ -0,0 +1,384 @@ +<?php +// +// +// utf7.inc - Routines to encode bytes to UTF7 and decode UTF7 strings +// +// Copyright (C) 1999, 2002 Ziberex and Torben Rybner +// +// +// Version 1.01 2002-06-08 19:00 +// +// - Adapted for use in IlohaMail (modified UTF-7 decoding) +// - Converted from C to PHP4 +// +// +// Version 1.00 1999-09-03 19:00 +// +// - Encodes bytes to UTF7 strings +// *OutString = '\0'; +// StartBase64Encode(); +// for (CP = InString; *CP; CP++) +// strcat(OutString, Base64Encode(*CP)); +// strcat(OutString, StopBase64Encode()); +// - Decodes Base64 strings to bytes +// StartBase64Decode(); +// for (CP1 = InString, CP2 = OutString; *CP1 && (*CP1 != '='); CP1++) +// CP2 += Base64Decode(*CP1, CP2); +// StopBase64Decode(); +// + +$BASE64LENGTH = 60; + +$BASE64DECODE_NO_DATA = -1; +$BASE64DECODE_EMPTY_DATA = -2; +$BASE64DECODE_INVALID_DATA = -3; + + +// +// +// Used for conversion to UTF7 +// +$_ToUTF7 = array +( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', ',' +); + +// +// +// Used for conversion from UTF7 +// (0x80 => Illegal, 0x40 => CR/LF) +// +$_FromUTF7 = array +( + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 00 - 07 - Ctrl - + 0x80, 0x80, 0x40, 0x80, 0x80, 0x40, 0x80, 0x80, // 08 - 0F - Ctrl - + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 10 - 17 - Ctrl - + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 18 - 1F - Ctrl - + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 20 - 27 !"#$%&' + 0x80, 0x80, 0x80, 0x3E, 0x3F, 0x80, 0x80, 0x3F, // 28 - 2F ()*+,-./ + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, // 30 - 37 01234567 + 0x3C, 0x3D, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, // 38 - 3F 89:;<=>? + 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 40 - 47 @ABCDEFG + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 48 - 4F HIJKLMNO + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 50 - 57 PQRSTUVW + 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80, // 58 - 5F XYZ[\]^_ + 0x80, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, // 60 - 67 `abcdefg + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 68 - 6F hijklmno + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 70 - 77 pqrstuvw + 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, // 78 - 7F xyz{|}~ +); + + +// +// +// UTF7EncodeInit: +// +// Start the encoding of bytes +// +function UTF7EncodeInit(&$Context) +{ + $Context[ "Data" ] = ""; + $Context[ "Count" ] = 0; + $Context[ "Pos" ] = 0; + $Context[ "State" ] = 0; +} // UTF7EncodeInit + + +// +// +// UTF7EncodeByte: +// +// Encodes one byte to UTF7 +// +function UTF7EncodeByte(&$Context, $Byte) +{ + global $_ToUTF7; + + $Byte = ord($Byte); + switch ($Context[ "State" ]) + { + case 0: + // Convert into a byte + $Context[ "Data" ] = $_ToUTF7[ $Byte >> 2 ]; + $Context[ "Pos" ]++; + // Save residue for next converted byte + $Context[ "Residue" ] = ($Byte & 0x03) << 4; + // This is the first byte in this line + $Context[ "Count" ] = 1; + // Next state is 1 + $Context[ "State" ] = 1; + break; + + case 1: + // Convert into a byte + $Context[ "Data" ] .= $_ToUTF7[ $Context[ "Residue" ] | ($Byte >> 4) ]; + $Context[ "Pos" ]++; + // Save residue for next converted byte + $Context[ "Residue" ] = ($Byte & 0x0F) << 2; + // Bumb byte counter + $Context[ "Count" ]++; + // Next state is 2 + $Context[ "State" ] = 2; + break; + + case 2: + // Convert into a byte + $Context[ "Data" ] .= $_ToUTF7[ $Context[ "Residue" ] | ($Byte >> 6) ]; + $Context[ "Pos" ]++; + // Residue fits precisely into the next byte + $Context[ "Data" ] .= $_ToUTF7[ $Byte & 0x3F ]; + $Context[ "Pos" ]++; + // Bumb byte counter + $Context[ "Count" ]++; + // Next state is 3 + $Context[ "State" ] = 3; + break; + + case 3: + // Convert into a byte + $Context[ "Data" ] .= $_ToUTF7[ $Byte >> 2 ]; + $Context[ "Pos" ]++; + // Save residue for next converted byte + $Context[ "Residue" ] = ($Byte & 0x03) << 4; + // Bumb byte counter + $Context[ "Count" ]++; + // Next state is 1 + $Context[ "State" ] = 1; + break; + + default: + // printf("Internal error in UTF7Encode: State is %d\n", $Context[ "State" ]); + // exit(1); + break; + } +} // UTF7EncodeByte + + +// +// +// UTF7EncodeFinal: +// +// Terminates the encoding of bytes +// +function UTF7EncodeFinal(&$Context) +{ + if ($Context[ "State" ] == 0) + return ""; + if ($Context[ "State" ] != 3) + UTF7EncodeByte($Context, "\0"); + return $Context[ "Data" ]; +} // UTF7EncodeFinal + + +// +// +// UTF7EncodeString +// +// Encodes a string to modified UTF-7 format +// +function UTF7EncodeString($String) +{ + // Not during encoding, yet + $Encoding = false; + // Go through the string + for ($I = 0; $I < strlen($String); $I++) + { + $Ch = substr($String, $I, 1); + if (ord($Ch) > 0x7F) + { + if (! $Encoding) + { + $RetVal .= "&"; + $Encoding = true; + // Initialise UTF7 context + UTF7EncodeInit($Context); + } + UTF7EncodeByte($Context, "\0"); + UTF7EncodeByte($Context, $Ch); + } + elseif ($Ch == "&") + { + if (! $Encoding) + { + $RetVal .= "&"; + $Encoding = true; + // Initialise UTF7 context + UTF7EncodeInit($Context); + } + else + { + UTF7EncodeByte($Context, "\0"); + UTF7EncodeByte($Context, $Ch); + } + } + else + { + if ($Encoding) + { + $RetVal .= UTF7EncodeFinal($Context) . "-$Ch"; + $Encoding = false; + } + else + $RetVal .= $Ch; + } + } + if ($Encoding) + $RetVal .= UTF7EncodeFinal($Context) . "-"; + return $RetVal; +} // UTF7EncodeString + + +// +// +// UTF7DecodeInit: +// +// Start the decoding of bytes +// +function UTF7DecodeInit(&$Context) +{ + $Context[ "Data" ] = ""; + $Context[ "State" ] = 0; + $Context[ "Pos" ] = 0; +} // UTF7DecodeInit + + +// +// +// UTF7DecodeByte: +// +// Decodes one character from UTF7 +// +function UTF7DecodeByte(&$Context, $Byte) +{ + global $BASE64DECODE_INVALID_DATA; + global $_FromUTF7; + + // Restore bits + $Byte = $_FromUTF7[ ord($Byte) ]; + // Ignore carriage returns and linefeeds + if ($Byte == 0x40) + return ""; + // Invalid byte - Tell caller! + if ($Byte == 0x80) + $Context[ "Count" ] = $BASE64DECODE_INVALID_DATA; + switch ($Context[ "State" ]) + { + case 0: + // Save residue + $Context[ "Residue" ] = $Byte; + // Initialise count + $Context[ "Count" ] = 0; + // Next state + $Context[ "State" ] = 1; + break; + + case 1: + // Store byte + $Context[ "Data" ] .= chr(($Context[ "Residue" ] << 2) | ($Byte >> 4)); + $Context[ "Pos" ]++; + // Update count + $Context[ "Count" ]++; + // Save residue + $Context[ "Residue" ] = $Byte; + // Next state + $Context[ "State" ] = 2; + break; + + case 2: + // Store byte + $Context[ "Data" ] .= chr(($Context[ "Residue" ] << 4) | ($Byte >> 2)); + $Context[ "Pos" ]++; + // Update count + $Context[ "Count" ]++; + // Save residue + $Context[ "Residue" ] = $Byte; + // Next state + $Context[ "State" ] = 3; + break; + + case 3: + // Store byte + $Context[ "Data" ] .= chr(($Context[ "Residue" ] << 6) | $Byte); + $Context[ "Pos" ]++; + // Update count + $Context[ "Count" ]++; + // Next state + $Context[ "State" ] = 4; + break; + + case 4: + // Save residue + $Context[ "Residue" ] = $Byte; + // Next state + $Context[ "State" ] = 1; + break; + } +} // UTF7DecodeByte + + +// +// +// UTF7DecodeFinal: +// +// Decodes one character from UTF7 +// +function UTF7DecodeFinal(&$Context) +{ + // Buffer not empty - Return remainder! + if ($Context[ "Count" ]) + { + $Context[ "Pos" ] = 0; + $Context[ "State" ] = 0; + return $Context[ "Data" ]; + } + return ""; +} // UTF7DecodeFinal + + +// +// +// UTF7DecodeString +// +// Converts a string encoded in modified UTF-7 encoding +// to ISO 8859-1. +// OBS: Works only for valid ISO 8859-1 characters in the +// encoded data +// +function UTF7DecodeString($String) +{ + $Decoding = false; + for ($I = 0; $I < strlen($String); $I++) + { + $Ch = substr($String, $I, 1); + if ($Decoding) + { + if ($Ch == "-") + { + $RetVal .= UTF7DecodeFinal($Context); + $Decoding = false; + } + else + UTF7DecodeByte($Context, $Ch); + } + elseif ($Ch == "&") + { + if (($I < strlen($String) - 1) && (substr($String, $I + 1, 1) == "-")) + { + $RetVal .= $Ch; + $I++; + } + else + { + UTF7DecodeInit($Context); + $Decoding = true; + } + } + else + $RetVal .= $Ch; + } + return str_replace("\0", "", $RetVal); +} // UTF7DecodeString +?> diff --git a/program/lib/utf8.inc b/program/lib/utf8.inc new file mode 100644 index 000000000..72a96b4e9 --- /dev/null +++ b/program/lib/utf8.inc @@ -0,0 +1,102 @@ +<?php +///////////////////////////// +// utf8.inc +// (C)2002 Ryo Chijiiwa <Ryo@IlohaMail.org> +// +// Description: +// UTF-8 handling functions +// +// This file is part of IlohaMail. IlohaMail is free software released +// under the GPL license. See enclosed file COPYING for details, or +// see http://www.fsf.org/copyleft/gpl.html +//////////////////////////// + +/** +* takes a string of utf-8 encoded characters and converts it to a string of unicode entities +* each unicode entitiy has the form &#nnnnn; n={0..9} and can be displayed by utf-8 supporting +* browsers +* @param $source string encoded using utf-8 [STRING] +* @return string of unicode entities [STRING] +* @access public +*/ +/** +* Author: ronen at greyzone dot com +* Taken from php.net comment: +* http://www.php.net/manual/en/function.utf8-decode.php +**/ +function utf8ToUnicodeEntities ($source) { + // array used to figure what number to decrement from character order value + // according to number of characters used to map unicode to ascii by utf-8 + $decrement[4] = 240; + $decrement[3] = 224; + $decrement[2] = 192; + $decrement[1] = 0; + + // the number of bits to shift each charNum by + $shift[1][0] = 0; + $shift[2][0] = 6; + $shift[2][1] = 0; + $shift[3][0] = 12; + $shift[3][1] = 6; + $shift[3][2] = 0; + $shift[4][0] = 18; + $shift[4][1] = 12; + $shift[4][2] = 6; + $shift[4][3] = 0; + + $pos = 0; + $len = strlen ($source); + $encodedString = ''; + while ($pos < $len) { + $asciiPos = ord (substr ($source, $pos, 1)); + if (($asciiPos >= 240) && ($asciiPos <= 255)) { + // 4 chars representing one unicode character + $thisLetter = substr ($source, $pos, 4); + $pos += 4; + } + else if (($asciiPos >= 224) && ($asciiPos <= 239)) { + // 3 chars representing one unicode character + $thisLetter = substr ($source, $pos, 3); + $pos += 3; + } + else if (($asciiPos >= 192) && ($asciiPos <= 223)) { + // 2 chars representing one unicode character + $thisLetter = substr ($source, $pos, 2); + $pos += 2; + } + else { + // 1 char (lower ascii) + $thisLetter = substr ($source, $pos, 1); + $pos += 1; + } + + // process the string representing the letter to a unicode entity + $thisLen = strlen ($thisLetter); + $thisPos = 0; + $decimalCode = 0; + while ($thisPos < $thisLen) { + $thisCharOrd = ord (substr ($thisLetter, $thisPos, 1)); + if ($thisPos == 0) { + $charNum = intval ($thisCharOrd - $decrement[$thisLen]); + $decimalCode += ($charNum << $shift[$thisLen][$thisPos]); + } + else { + $charNum = intval ($thisCharOrd - 128); + $decimalCode += ($charNum << $shift[$thisLen][$thisPos]); + } + + $thisPos++; + } + + if ($thisLen == 1) + $encodedLetter = "&#". str_pad($decimalCode, 3, "0", STR_PAD_LEFT) . ';'; + else + $encodedLetter = "&#". str_pad($decimalCode, 5, "0", STR_PAD_LEFT) . ';'; + + $encodedString .= $encodedLetter; + } + + return $encodedString; +} + +?>
\ No newline at end of file diff --git a/program/localization/de/labels.inc b/program/localization/de/labels.inc new file mode 100644 index 000000000..8c50e1052 --- /dev/null +++ b/program/localization/de/labels.inc @@ -0,0 +1,171 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | language/de/labels.inc | + | | + | Language file of the RoundCube Webmail client | + | Copyright (C) 2005, RoundQube Dev. - Switzerland | + | All rights reserved. | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$labels = array(); + +// login page +$labels['username'] = 'Benutzername'; +$labels['password'] = 'Passwort'; +$labels['server'] = 'Server'; +$labels['login'] = 'Login'; + +// taskbar +$labels['logout'] = 'Logout'; +$labels['mail'] = 'E-Mail'; +$labels['settings'] = 'Einstellungen'; +$labels['addressbook'] = 'Adressbuch'; + +// mailbox names +$labels['inbox'] = 'Posteingang'; +$labels['sent'] = 'Gesendet'; +$labels['trash'] = 'Gelöscht'; +$labels['drafts'] = 'Vorlagen'; +$labels['junk'] = 'Junk'; + +// message listing +$labels['subject'] = 'Betreff'; +$labels['from'] = 'Absender'; +$labels['to'] = 'Empfänger'; +$labels['cc'] = 'Kopie'; +$labels['bcc'] = 'Bcc'; +$labels['replyto'] = 'Antwort an'; +$labels['date'] = 'Datum'; +$labels['size'] = 'Grösse'; +$labels['priority'] = 'Priorität'; +$labels['organization'] = 'Organisation'; + +// aliases +$labels['reply-to'] = $labels['replyto']; + +$labels['mailboxlist'] = 'Ordner'; +$labels['messagesfromto'] = 'Nachrichten $from bis $to von $count'; +$labels['messagenrof'] = 'Nachrichten $nr von $count'; + +$labels['moveto'] = 'verschieben nach...'; +$labels['download'] = 'download'; + +$labels['filename'] = 'Dateiname'; +$labels['filesize'] = 'Dateigrösse'; + +$labels['preferhtml'] = 'HTML bevorzugen'; +$labels['htmlmessage'] = 'HTML Nachricht'; + +$labels['addtoaddressbook'] = 'Ins Adressbuch übernehmen'; + +// weekdays short +$labels['sun'] = 'So'; +$labels['mon'] = 'Mo'; +$labels['tue'] = 'Di'; +$labels['wed'] = 'Mi'; +$labels['thu'] = 'Do'; +$labels['fri'] = 'Fr'; +$labels['sat'] = 'Sa'; + +// weekdays long +$labels['sunday'] = 'Sonntag'; +$labels['monday'] = 'Montag'; +$labels['tuesday'] = 'Dienstag'; +$labels['wednesday'] = 'Mittwoch'; +$labels['thursday'] = 'Donnerstag'; +$labels['friday'] = 'Freitag'; +$labels['saturday'] = 'Samstag'; + +$labels['today'] = 'Heute'; + +// toolbar buttons +$labels['writenewmessage'] = 'Neue Nachricht schreiben'; +$labels['replytomessage'] = 'Antwort verfassen'; +$labels['forwardmessage'] = 'Nachricht weiterleiten'; +$labels['deletemessage'] = 'In den Papierkorb verschieben'; +$labels['printmessage'] = 'Diese Nachricht drucken'; +$labels['previousmessages'] = 'Vorherige Nachrichten anzeigen'; +$labels['nextmessages'] = 'Weitere Nachrichten anzeigen'; +$labels['backtolist'] = 'Zurück zur Liste'; + +$labels['select'] = 'Auswählen'; +$labels['all'] = 'Alle'; +$labels['none'] = 'Keine'; +$labels['unread'] = 'Ungelesene'; + +// message compose +$labels['compose'] = 'Neue Nachricht verfassen'; +$labels['sendmessage'] = 'Nachricht jetzt senden'; +$labels['addattachment'] = 'Datei anfügen'; + +$labels['upload'] = 'Hochladen'; +$labels['close'] = 'Schliessen'; + +$labels['low'] = 'Tief'; +$labels['lowest'] = 'Tiefste'; +$labels['normal'] = 'Normal'; +$labels['high'] = 'Hoch'; +$labels['highest'] = 'Höchste'; + +$labels['showimages'] = 'Bilder anzeigen'; + + +// address boook +$labels['name'] = 'Anzeigename'; +$labels['firstname'] = 'Vorname'; +$labels['surname'] = 'Nachname'; +$labels['email'] = 'E-Mail'; + +$labels['addcontact'] = 'Kontakt hinzufügen'; +$labels['editcontact'] = 'Kontakt bearbeiten'; + +$labels['edit'] = 'Bearbeiten'; +$labels['cancel'] = 'Abbrechen'; +$labels['save'] = 'Speichern'; +$labels['delete'] = 'Löschen'; + +$labels['newcontact'] = 'Neuen Kontakt erfassen'; +$labels['deletecontact'] = 'Gewählte Kontakte löschen'; +$labels['composeto'] = 'Nachricht verfassen'; +$labels['contactsfromto'] = 'Kontakte $from bis $to von $count'; + + +// settings +$labels['settingsfor'] = 'Einstellungen für'; + +$labels['preferences'] = 'Einstellungen'; +$labels['userpreferences'] = 'Benutzereinstellungen'; +$labels['editpreferences'] = 'Ereinstellungen bearbeiten'; + +$labels['identities'] = 'Absender'; +$labels['manageidentities'] = 'Absender für dieses Konto verwalten'; +$labels['newidentity'] = 'Neuer Absender'; + +$labels['newitem'] = 'Neuer Eintrag'; +$labels['edititem'] = 'Eintrag bearbeiten'; + +$labels['setdefault'] = 'Als Standard'; +$labels['language'] = 'Sprache'; +$labels['timezone'] = 'Zeitzone'; +$labels['pagesize'] = 'Einträge pro Seite'; + + +$labels['folders'] = 'Ordner'; +$labels['foldername'] = 'Ordnername'; +$labels['subscribed'] = 'Abonniert'; +$labels['create'] = 'Erstellen'; +$labels['createfolder'] = 'Neuen Ordner erstellen'; +$labels['deletefolder'] = 'Ordner löschen'; +$labels['managefolders'] = 'Ordner verwalten'; + + +?>
\ No newline at end of file diff --git a/program/localization/de/messages.inc b/program/localization/de/messages.inc new file mode 100644 index 000000000..518d6e3e1 --- /dev/null +++ b/program/localization/de/messages.inc @@ -0,0 +1,56 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | language/de/messages.inc | + | | + | Language file of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$messages = array(); + +$messages['loginfailed'] = 'Login fehlgeschlagen'; + +$messages['cookiesdisabled'] = 'Ihr Browser akzeptiert keine Cookies'; + +$messages['sessionerror'] = 'Ihre Session ist ungültig oder abgelaufen'; + +$messages['imaperror'] = 'Keine Verbindung zum IMAP server'; + +$messages['nomessagesfound'] = 'Keine Nachrichten in diesem Order'; + +$messages['loggedout'] = 'Sie haben Ihre Session erfolgreich beendeet. Auf Wiedersehen!'; + +$messages['mailboxempty'] = 'Ordner ist leer'; + +$messages['loadingdata'] = 'Daten werden geladen...'; + +$messages['messagesent'] = 'Nachricht erfolgreich gesendet'; + +$messages['successfullysaved'] = 'Erfolgreich gespeichert'; + +$messages['addedsuccessfully'] = 'Kontakt zum Adressbuch hinzugefügt'; + +$messages['contactexists'] = 'Es existiert bereits ein Eintrag mit dieser E-Mail-Adresse'; + +$messages['blockedimages'] = 'Um Ihre Privatsphäre zur schützen, wurden externe Bilder blockiert.'; + +$messages['encryptedmessage'] = 'Dies ist eine verschlüsselte Nachricht und kann leider nicht angezeigt werden.'; + +$messages['nocontactsfound'] = 'Keine Kontakte gefunden'; + +$messages['sendingfailed'] = 'Versand der Nachricht fehlgeschlagen'; + +$messages['errorsaving'] = 'Beim Speichern ist ein Fehler aufgetreten'; + + +?>
\ No newline at end of file diff --git a/program/localization/en/labels.inc b/program/localization/en/labels.inc new file mode 100644 index 000000000..7dd0fc1df --- /dev/null +++ b/program/localization/en/labels.inc @@ -0,0 +1,171 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | language/en/labels.inc | + | | + | Language file of the RoundCube Webmail client | + | Copyright (C) 2005, RoundQube Dev. - Switzerland | + | All rights reserved. | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$labels = array(); + +// login page +$labels['username'] = 'Username'; +$labels['password'] = 'Password'; +$labels['server'] = 'Server'; +$labels['login'] = 'Login'; + +// taskbar +$labels['logout'] = 'Logout'; +$labels['mail'] = 'E-Mail'; +$labels['settings'] = 'Personal Settings'; +$labels['addressbook'] = 'Address Book'; + +// mailbox names +$labels['inbox'] = 'Inbox'; +$labels['sent'] = 'Sent'; +$labels['trash'] = 'Trash'; +$labels['drafts'] = 'Drafts'; +$labels['junk'] = 'Junk'; + +// message listing +$labels['subject'] = 'Subject'; +$labels['from'] = 'Sender'; +$labels['to'] = 'Recipient'; +$labels['cc'] = 'Copy'; +$labels['bcc'] = 'Bcc'; +$labels['replyto'] = 'Reply-To'; +$labels['date'] = 'Date'; +$labels['size'] = 'Size'; +$labels['priority'] = 'Priority'; +$labels['organization'] = 'Organization'; + +// aliases +$labels['reply-to'] = $labels['replyto']; + +$labels['mailboxlist'] = 'Folders'; +$labels['messagesfromto'] = 'Messages $from to $to of $count'; +$labels['messagenrof'] = 'Message $nr of $count'; + +$labels['moveto'] = 'move to...'; +$labels['download'] = 'download'; + +$labels['filename'] = 'File name'; +$labels['filesize'] = 'File size'; + +$labels['preferhtml'] = 'Prefer HTML'; +$labels['htmlmessage'] = 'HTML Message'; + +$labels['addtoaddressbook'] = 'Add to address book'; + +// weekdays short +$labels['sun'] = 'Sun'; +$labels['mon'] = 'Mon'; +$labels['tue'] = 'Tue'; +$labels['wed'] = 'Wed'; +$labels['thu'] = 'Thu'; +$labels['fri'] = 'Fri'; +$labels['sat'] = 'Sat'; + +// weekdays long +$labels['sunday'] = 'Sunday'; +$labels['monday'] = 'Monday'; +$labels['tuesday'] = 'Tuesday'; +$labels['wednesday'] = 'Wednesday'; +$labels['thursday'] = 'Thursday'; +$labels['friday'] = 'Friday'; +$labels['saturday'] = 'Saturday'; + +$labels['today'] = 'Today'; + +// toolbar buttons +$labels['writenewmessage'] = 'Create a new message'; +$labels['replytomessage'] = 'Reply to the message'; +$labels['forwardmessage'] = 'Forwad the message'; +$labels['deletemessage'] = 'Move message to trash'; +$labels['printmessage'] = 'Print this message'; +$labels['previousmessages'] = 'Show previous set of messages'; +$labels['nextmessages'] = 'Show next set of messages'; +$labels['backtolist'] = 'Back to message list'; + +$labels['select'] = 'Select'; +$labels['all'] = 'All'; +$labels['none'] = 'None'; +$labels['unread'] = 'Unread'; + +// message compose +$labels['compose'] = 'Compose a message'; +$labels['sendmessage'] = 'Send the message now'; +$labels['addattachment'] = 'Attach a file'; + +$labels['upload'] = 'Upload'; +$labels['close'] = 'Close'; + +$labels['low'] = 'Low'; +$labels['lowest'] = 'Lowest'; +$labels['normal'] = 'Normal'; +$labels['high'] = 'High'; +$labels['highest'] = 'Highest'; + +$labels['showimages'] = 'Display images'; + + +// address boook +$labels['name'] = 'Display name'; +$labels['firstname'] = 'First name'; +$labels['surname'] = 'Last name'; +$labels['email'] = 'E-Mail'; + +$labels['addcontact'] = 'Add new contact'; +$labels['editcontact'] = 'Edit contact'; + +$labels['edit'] = 'Edit'; +$labels['cancel'] = 'Cancel'; +$labels['save'] = 'Save'; +$labels['delete'] = 'Delete'; + +$labels['newcontact'] = 'Create new contact card'; +$labels['deletecontact'] = 'Delete selected contacts'; +$labels['composeto'] = 'Compose mail to'; +$labels['contactsfromto'] = 'Contacts $from to $to of $count'; + + +// settings +$labels['settingsfor'] = 'Settings for'; + +$labels['preferences'] = 'Preferences'; +$labels['userpreferences'] = 'User preferences'; +$labels['editpreferences'] = 'Edit user preferences'; + +$labels['identities'] = 'Identities'; +$labels['manageidentities'] = 'Manage identities for this account'; +$labels['newidentity'] = 'New identity'; + +$labels['newitem'] = 'New item'; +$labels['edititem'] = 'Edit item'; + +$labels['setdefault'] = 'Set default'; +$labels['language'] = 'Language'; +$labels['timezone'] = 'Time zone'; +$labels['pagesize'] = 'Rows per page'; + + +$labels['folders'] = 'Folders'; +$labels['foldername'] = 'Folder name'; +$labels['subscribed'] = 'Subscribed'; +$labels['create'] = 'Create'; +$labels['createfolder'] = 'Create new folder'; +$labels['deletefolder'] = 'Delete folder'; +$labels['managefolders'] = 'Manage folders'; + + +?>
\ No newline at end of file diff --git a/program/localization/en/messages.inc b/program/localization/en/messages.inc new file mode 100644 index 000000000..68273931e --- /dev/null +++ b/program/localization/en/messages.inc @@ -0,0 +1,56 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | language/en/messages.inc | + | | + | Language file of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$messages = array(); + +$messages['loginfailed'] = 'Login failed'; + +$messages['cookiesdisabled'] = 'Your browser does not accept cookies'; + +$messages['sessionerror'] = 'Your session is invalid or expired'; + +$messages['imaperror'] = 'Connection to IMAP server failed'; + +$messages['nomessagesfound'] = 'No messages found in this mailbox'; + +$messages['loggedout'] = 'You have successfully terminated the session. Goody bye!'; + +$messages['mailboxempty'] = 'Mailbox is empty'; + +$messages['loadingdata'] = 'Loading data...'; + +$messages['messagesent'] = 'Message sent successfully'; + +$messages['successfullysaved'] = 'Successfully saved'; + +$messages['addedsuccessfully'] = 'Contact added successfully to address book'; + +$messages['contactexists'] = 'A contact with this e-mail address already exists'; + +$messages['blockedimages'] = 'To protect your privacy, remote images are blocked in this message.'; + +$messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!'; + +$messages['nocontactsfound'] = 'No contacts found'; + +$messages['sendingfailed'] = 'Failed to send message'; + +$messages['errorsaving'] = 'An error occured while saving'; + + +?>
\ No newline at end of file diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc new file mode 100644 index 000000000..99d9e33d4 --- /dev/null +++ b/program/steps/addressbook/delete.inc @@ -0,0 +1,104 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/delete.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Delete the submitted contacts (CIDs) from the users address book | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = TRUE; + +if ($_GET['_cid']) + { + $DB->query(sprintf("UPDATE %s + SET del='1' + WHERE user_id=%d + AND contact_id IN (%s)", + get_table_name('contacts'), + $_SESSION['user_id'], + $_GET['_cid'])); + + $count = $DB->affected_rows(); + if (!$count) + { + // send error message + exit; + } + + + // count contacts for this user + $sql_result = $DB->query(sprintf("SELECT COUNT(contact_id) AS rows + FROM %s + WHERE del!='1' + AND user_id=%d", + get_table_name('contacts'), + $_SESSION['user_id'])); + + $sql_arr = $DB->fetch_assoc($sql_result); + $rowcount = $sql_arr['rows']; + + // update message count display + $pages = ceil($rowcount/$CONFIG['pagesize']); + $commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount)); + $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages); + + + // add new rows from next page (if any) + if ($_GET['_from']!='show' && $pages>1 && $_SESSION['page'] < $pages) + { + $start_row = ($_SESSION['page'] * $CONFIG['pagesize']) - $count; + + // get contacts from DB + $sql_result = $DB->query(sprintf("SELECT * FROM %s + WHERE del!='1' + AND user_id=%d + ORDER BY name + LIMIT %d, %d", + get_table_name('contacts'), + $_SESSION['user_id'], + $start_row, + $count)); + + $commands .= rcmail_js_contacts_list($sql_result); + +/* + // define list of cols to be displayed + $a_show_cols = array('name', 'email'); + + while ($sql_arr = $DB->fetch_assoc($sql_result)) + { + $a_row_cols = array(); + + // format each col + foreach ($a_show_cols as $col) + { + $cont = rep_specialchars_output($sql_arr[$col]); + $a_row_cols[$col] = $cont; + } + + $commands .= sprintf("this.add_contact_row(%s, %s);\n", + $sql_arr['contact_id'], + array2js($a_row_cols)); + } +*/ + } + + // send response + rcube_remote_response($commands); + } + +exit; +?>
\ No newline at end of file diff --git a/program/steps/addressbook/edit.inc b/program/steps/addressbook/edit.inc new file mode 100644 index 000000000..db7b77a59 --- /dev/null +++ b/program/steps/addressbook/edit.inc @@ -0,0 +1,123 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/edit.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Show edit form for a contact entry or to add a new one | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +if (($_GET['_cid'] || $_POST['_cid']) && $_action=='edit') + { + $cid = $_POST['_cid'] ? $_POST['_cid'] : $_GET['_cid']; + $DB->query(sprintf("SELECT * FROM %s + WHERE contact_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('contacts'), + $cid, + $_SESSION['user_id'])); + + $CONTACT_RECORD = $DB->fetch_assoc(); + + if (is_array($CONTACT_RECORD)) + $OUTPUT->add_script(sprintf("%s.set_env('cid', '%s');", $JS_OBJECT_NAME, $CONTACT_RECORD['contact_id'])); + } + + + +function rcmail_contact_editform($attrib) + { + global $CONTACT_RECORD, $JS_OBJECT_NAME; + + if (!$CONTACT_RECORD && $GLOBALS['_action']!='add') + return rcube_label('contactnotfound'); + + list($form_start, $form_end) = get_form_tags($attrib); + unset($attrib['form']); + + + // a specific part is requested + if ($attrib['part']) + { + $out = $form_start; + $out .= rcmail_get_edit_field($attrib['part'], $CONTACT_RECORD[$attrib['part']], $attrib); + return $out; + } + + + // return the complete address edit form as table + $out = "$form_start<table>\n\n"; + + $a_show_cols = array('name', 'firstname', 'surname', 'email'); + foreach ($a_show_cols as $col) + { + $attrib['id'] = 'rcmfd_'.$col; + $title = rcube_label($col); + $value = rcmail_get_edit_field($col, $CONTACT_RECORD[$col], $attrib); + $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", + $attrib['id'], + $title, + $value); + } + + $out .= "\n</table>$form_end"; + + return $out; + } + + +// similar function as in /steps/settings/edit_identity.inc +function get_form_tags($attrib) + { + global $CONTACT_RECORD, $OUTPUT, $JS_OBJECT_NAME, $EDIT_FORM, $SESS_HIDDEN_FIELD; + + $form_start = ''; + if (!strlen($EDIT_FORM)) + { + $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task'])); + $hiddenfields->add(array('name' => '_action', 'value' => 'save')); + + if ($_GET['_framed'] || $_POST['_framed']) + $hiddenfields->add(array('name' => '_framed', 'value' => 1)); + + if ($CONTACT_RECORD['contact_id']) + $hiddenfields->add(array('name' => '_cid', 'value' => $CONTACT_RECORD['contact_id'])); + + $form_start = !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : ''; + $form_start .= "\n$SESS_HIDDEN_FIELD\n"; + $form_start .= $hiddenfields->show(); + } + + $form_end = (strlen($EDIT_FORM) && !strlen($attrib['form'])) ? '</form>' : ''; + $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form'; + + if (!strlen($EDIT_FORM)) + $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('editform', '$form_name');"); + + $EDIT_FORM = $form_name; + + return array($form_start, $form_end); + } + + + +if (!$CONTACT_RECORD && template_exists('addcontact')) + parse_template('addcontact'); + +// this will be executed if no template for addcontact exists +parse_template('editcontact'); +?>
\ No newline at end of file diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc new file mode 100644 index 000000000..309c2a3a2 --- /dev/null +++ b/program/steps/addressbook/func.inc @@ -0,0 +1,198 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/func.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide addressbook functionality and GUI objects | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$CONTACTS_LIST = array(); + +// set list properties and session vars +if (strlen($_GET['_page'])) + { + $CONTACTS_LIST['page'] = $_GET['_page']; + $_SESSION['page'] = $_GET['_page']; + } +else + $CONTACTS_LIST['page'] = $_SESSION['page'] ? $_SESSION['page'] : 1; + + + +// return the message list as HTML table +function rcmail_contacts_list($attrib) + { + global $DB, $CONFIG, $OUTPUT, $CONTACTS_LIST, $JS_OBJECT_NAME; + + //$skin_path = $CONFIG['skin_path']; + //$image_tag = '<img src="%s%s" alt="%s" border="0" />'; + + // count contacts for this user + $sql_result = $DB->query(sprintf("SELECT COUNT(contact_id) AS rows + FROM %s + WHERE del!='1' + AND user_id=%d", + get_table_name('contacts'), + $_SESSION['user_id'])); + + $sql_arr = $DB->fetch_assoc($sql_result); + $rowcount = $sql_arr['rows']; + + if ($rowcount) + { + $start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize']; + + // get contacts from DB + $sql_result = $DB->query(sprintf("SELECT * FROM %s + WHERE del!='1' + AND user_id=%d + ORDER BY name + LIMIT %d, %d", + get_table_name('contacts'), + $_SESSION['user_id'], + $start_row, + $CONFIG['pagesize'])); + } + else + $sql_result = NULL; + + + // add id to message list table if not specified + if (!strlen($attrib['id'])) + $attrib['id'] = 'rcmAddressList'; + + // define list of cols to be displayed + $a_show_cols = array('name', 'email'); + + // create XHTML table + $out = rcube_table_output($attrib, $sql_result, $a_show_cols, 'contact_id'); + + // set client env + $javascript = sprintf("%s.gui_object('contactslist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']); + $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $CONTACTS_LIST['page']); + $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($rowcount/$CONFIG['pagesize'])); + //$javascript .= sprintf("%s.set_env('contacts', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr)); + + $OUTPUT->add_script($javascript); + + return $out; + } + + + +function rcmail_js_contacts_list($sql_result, $obj_name='this') + { + global $DB; + + $commands = ''; + + if (!$sql_result) + return ''; + + // define list of cols to be displayed + $a_show_cols = array('name', 'email'); + + while ($sql_arr = $DB->fetch_assoc($sql_result)) + { + $a_row_cols = array(); + + // format each col + foreach ($a_show_cols as $col) + { + $cont = rep_specialchars_output($sql_arr[$col]); + $a_row_cols[$col] = $cont; + } + + $commands .= sprintf("%s.add_contact_row(%s, %s);\n", + $obj_name, + $sql_arr['contact_id'], + array2js($a_row_cols)); + } + + return $commands; + } + + +// similar function as /steps/settings/identities.inc::rcmail_identity_frame() +function rcmail_contact_frame($attrib) + { + global $OUTPUT, $JS_OBJECT_NAME; + + if (!$attrib['id']) + $attrib['id'] = 'rcmcontactframe'; + + $attrib['name'] = $attrib['id']; + + $OUTPUT->add_script(sprintf("%s.set_env('contentframe', '%s');", $JS_OBJECT_NAME, $attrib['name'])); + + $attrib_str = create_attrib_string($attrib, array('name', 'id', 'class', 'style', 'src', 'width', 'height', 'frameborder')); + $out = '<iframe'. $attrib_str . '></iframe>'; + + return $out; + } + + +function rcmail_rowcount_display($attrib) + { + global $OUTPUT, $JS_OBJECT_NAME; + + if (!$attrib['id']) + $attrib['id'] = 'rcmcountdisplay'; + + $OUTPUT->add_script(sprintf("%s.gui_object('countdisplay', '%s');", $JS_OBJECT_NAME, $attrib['id'])); + + // allow the following attributes to be added to the <span> tag + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); + + + $out = '<span' . $attrib_str . '>'; + $out .= rcmail_get_rowcount_text(); + $out .= '</span>'; + return $out; + } + + + +function rcmail_get_rowcount_text($max=NULL) + { + global $CONTACTS_LIST, $CONFIG, $DB; + + $start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize'] + 1; + + // get nr of contacts + if ($max===NULL) + { + $sql_result = $DB->query(sprintf("SELECT 1 FROM %s + WHERE del!='1' + AND user_id=%d", + get_table_name('contacts'), + $_SESSION['user_id'])); + + $max = $DB->num_rows($sql_result); + } + + if ($max==0) + $out = rcube_label('nocontactsfound'); + else + $out = rcube_label(array('name' => 'contactsfromto', + 'vars' => array('from' => $start_row, + 'to' => min($max, $start_row + $CONFIG['pagesize'] - 1), + 'count' => $max))); + + return $out; + } + +?>
\ No newline at end of file diff --git a/program/steps/addressbook/list.inc b/program/steps/addressbook/list.inc new file mode 100644 index 000000000..87ac888de --- /dev/null +++ b/program/steps/addressbook/list.inc @@ -0,0 +1,59 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/list.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Send contacts list to client (as remote response) | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = TRUE; + +// count contacts for this user +$sql_result = $DB->query(sprintf("SELECT COUNT(contact_id) AS rows + FROM %s + WHERE del!='1' + AND user_id=%d", + get_table_name('contacts'), + $_SESSION['user_id'])); + +$sql_arr = $DB->fetch_assoc($sql_result); +$rowcount = $sql_arr['rows']; + +// update message count display +$pages = ceil($rowcount/$CONFIG['pagesize']); +$commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount)); +$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages); + +$start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize']; + +// get contacts from DB +$sql_result = $DB->query(sprintf("SELECT * FROM %s + WHERE del!='1' + AND user_id=%d + ORDER BY name + LIMIT %d, %d", + get_table_name('contacts'), + $_SESSION['user_id'], + $start_row, + $CONFIG['pagesize'])); + +$commands .= rcmail_js_contacts_list($sql_result); + +// send response +rcube_remote_response($commands); + +exit; +?>
\ No newline at end of file diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc new file mode 100644 index 000000000..c0afd23d8 --- /dev/null +++ b/program/steps/addressbook/save.inc @@ -0,0 +1,168 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/save.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Save a contact entry or to add a new one | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +$a_save_cols = array('name', 'firstname', 'surname', 'email'); + + +// update an existing contact +if ($_POST['_cid']) + { + $a_write_sql = array(); + + foreach ($a_save_cols as $col) + { + $fname = '_'.$col; + if (!isset($_POST[$fname])) + continue; + + $a_write_sql[] = sprintf("%s='%s'", $col, addslashes($_POST[$fname])); + } + + if (sizeof($a_write_sql)) + { + $DB->query(sprintf("UPDATE %s + SET %s + WHERE contact_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('contacts'), + join(', ', $a_write_sql), + $_POST['_cid'], + $_SESSION['user_id'])); + + $updated = $DB->affected_rows(); + } + + if ($updated) + { + $_action = 'show'; + show_message('successfullysaved', 'confirmation'); + + if ($_POST['_framed']) + { + // define list of cols to be displayed + $a_show_cols = array('name', 'email'); + $a_js_cols = array(); + + $sql_result = $DB->query(sprintf("SELECT * FROM %s + WHERE contact_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('contacts'), + $_POST['_cid'], + $_SESSION['user_id'])); + + $sql_arr = $DB->fetch_assoc($sql_result); + foreach ($a_show_cols as $col) + $a_js_cols[] = (string)$sql_arr[$col]; + + // update the changed col in list + $OUTPUT->add_script(sprintf("if(parent.%s)parent.%s.update_contact_row('%d', %s);", + $JS_OBJECT_NAME, + $JS_OBJECT_NAME, + $_POST['_cid'], + array2js($a_js_cols))); + + // show confirmation + show_message('successfullysaved', 'confirmation'); + } + } + else + { + // show error message + show_message('errorsaving', 'error'); + $_action = 'show'; + } + } + +// insert a new contact +else + { + $a_insert_cols = $a_insert_values = array(); + + foreach ($a_save_cols as $col) + { + $fname = '_'.$col; + if (!isset($_POST[$fname])) + continue; + + $a_insert_cols[] = $col; + $a_insert_values[] = sprintf("'%s'", addslashes($_POST[$fname])); + } + + if (sizeof($a_insert_cols)) + { + $DB->query(sprintf("INSERT INTO %s + (user_id, %s) + VALUES (%d, %s)", + get_table_name('contacts'), + join(', ', $a_insert_cols), + $_SESSION['user_id'], + join(', ', $a_insert_values))); + + $insert_id = $DB->insert_id(); + } + + if ($insert_id) + { + $_action = 'show'; + $_GET['_cid'] = $insert_id; + + if ($_POST['_framed']) + { + // add contact row or jump to the page where it should appear + $commands = sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME); + $sql_result = $DB->query(sprintf("SELECT * FROM %s + WHERE contact_id=%d + AND user_id=%d", + get_table_name('contacts'), + $insert_id, + $_SESSION['user_id'])); + $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME); + + $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n", + $JS_OBJECT_NAME, + $JS_OBJECT_NAME, + $insert_id); + + // update record count display + $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n", + $JS_OBJECT_NAME, + $JS_OBJECT_NAME, + rcmail_get_rowcount_text()); + + $OUTPUT->add_script($commands); + + // show confirmation + show_message('successfullysaved', 'confirmation'); + } + } + else + { + // show error message + show_message('errorsaving', 'error'); + $_action = 'add'; + } + } + + +?>
\ No newline at end of file diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc new file mode 100644 index 000000000..9317645d0 --- /dev/null +++ b/program/steps/addressbook/show.inc @@ -0,0 +1,81 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/addressbook/show.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Show contact details | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +if ($_GET['_cid'] || $_POST['_cid']) + { + $cid = $_POST['_cid'] ? $_POST['_cid'] : $_GET['_cid']; + $DB->query(sprintf("SELECT * FROM %s + WHERE contact_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('contacts'), + $cid, + $_SESSION['user_id'])); + + $CONTACT_RECORD = $DB->fetch_assoc(); + + if (is_array($CONTACT_RECORD)) + $OUTPUT->add_script(sprintf("%s.set_env('cid', '%s');", $JS_OBJECT_NAME, $CONTACT_RECORD['contact_id'])); + } + + + +function rcmail_contact_details($attrib) + { + global $CONTACT_RECORD, $JS_OBJECT_NAME; + + if (!$CONTACT_RECORD) + return show_message('contactnotfound'); + + // a specific part is requested + if ($attrib['part']) + return rep_specialchars_output($CONTACT_RECORD[$attrib['part']]); + + + // return the complete address record as table + $out = "<table>\n\n"; + + $a_show_cols = array('name', 'firstname', 'surname', 'email'); + foreach ($a_show_cols as $col) + { + if ($col=='email' && $CONTACT_RECORD[$col]) + $value = sprintf('<a href="#compose" onclick="%s.command(\'compose\', %d)" title="%s">%s</a>', + $JS_OBJECT_NAME, + $CONTACT_RECORD['contact_id'], + rcube_label('composeto'), + $CONTACT_RECORD[$col]); + else + $value = rep_specialchars_output($CONTACT_RECORD[$col]); + + $title = rcube_label($col); + $out .= sprintf("<tr><td class=\"title\">%s</td><td>%s</td></tr>\n", $title, $value); + } + + + $out .= "\n</table>"; + + return $out; + } + + +parse_template('showcontact'); +?>
\ No newline at end of file diff --git a/program/steps/error.inc b/program/steps/error.inc new file mode 100644 index 000000000..f2d639bf6 --- /dev/null +++ b/program/steps/error.inc @@ -0,0 +1,117 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/error.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Display error message page | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +// browser is not compatible with this application +if ($ERROR_CODE==409) + { + $user_agent = $GLOBALS['HTTP_SERVER_VARS']['HTTP_USER_AGENT']; + $__error_title = 'Your browser does not suit the requirements for this application'; + $__error_text = <<<EOF +<i>Supported browsers:</i><br /> +» Netscape 7+<br /> +» Microsoft Internet Explorer 6+<br /> +» Mozilla Firefox 1.0+<br /> +» Opera 8.0+<br /> +» Safari 1.2+<br /> +<br /> +» JavaScript enabled<br /> + +<p><i>Your configuration:</i><br /> +$user_agent</p> +EOF; + } + +// authorization error +else if ($ERROR_CODE==401) + { + $__error_title = "AUTHORIZATION FAILED"; + $__error_text = "Could not verify that you are authorized to access this service!<br />\n". + "Please contact your server-administrator."; + } + +// failed request (wrong step in URL) +else if ($ERROR_CODE==404) + { + $__error_title = "REQUEST FAILED/FILE NOT FOUND"; + $request_url = $GLOBALS['HTTP_HOST'].$GLOBALS['REQUEST_URI']; + $__error_text = <<<EOF +The requested page was not found!<br /> +Please contact your server-administrator. + +<p><i>Failed request:</i><br /> +http://$request_url</p> +EOF; + } + + +// system error +else + { + $__error_title = "SERVICE CURRENTLY NOT AVAILABLE!"; + $__error_text = "Please contact your server-administrator."; + + if (($CONFIG['debug_level'] & 4) && $ERROR_MESSAGE) + $__error_text = $ERROR_MESSAGE; + else + $__error_text = 'Error No. '.dechex($ERROR_CODE).')'; + } + + +// compose page content + +$__page_content = <<<EOF +<div> +<h3 class="error-title">$__error_title</h3> +<p class="error-text">$__error_text</p> +</div> +EOF; + + + +if (template_exists('error')) + { + $OUTPUT->scripts = array(); + $OUTPUT->script_files = array(); + parse_template('error'); + } + + +// print system error page +print <<<EOF +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"><head> +<title>RoundCube|Mail : ERROR $ERROR_CODE</title> +<link rel="stylesheet" type="text/css" href="program/style.css" /> +</head> +<body> + +<table border="0" cellsapcing="0" cellpadding="0" width="100%" height="80%"><tr><td align="center"> + +$__page_content + +</td></tr></table> + +</body> +</html> +EOF; + +?>
\ No newline at end of file diff --git a/program/steps/mail/addcontact.inc b/program/steps/mail/addcontact.inc new file mode 100644 index 000000000..7f1102412 --- /dev/null +++ b/program/steps/mail/addcontact.inc @@ -0,0 +1,70 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/addcontact.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Add the submitted contact to the users address book | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = TRUE; + +if ($_GET['_address']) + { + $contact_arr = $IMAP->decode_address_list($_GET['_address']); + if (sizeof($contact_arr)) + { + $contact = $contact_arr[1]; + + if ($contact['mailto']) + $sql_result = $DB->query(sprintf("SELECT 1 FROM %s + WHERE user_id=%d + AND email='%s' + AND del!='1'", + get_table_name('contacts'), + $_SESSION['user_id'], + $contact['mailto'])); + + // contact entry with this mail address exists + if ($sql_result && $DB->num_rows($sql_result)) + $existing_contact = TRUE; + + else if ($contact['mailto']) + { + $DB->query(sprintf("INSERT INTO %s + (user_id, name, email) + VALUES (%d, '%s', '%s')", + get_table_name('contacts'), + $_SESSION['user_id'], + $contact['name'], + $contact['mailto'])); + + $added = $DB->insert_id(); + } + } + + if ($added) + $commands = show_message('addedsuccessfully', 'confirmation'); + else if ($existing_contact) + $commands = show_message('contactexists', 'warning'); + } + + +if (!$commands) + $commands = show_message('errorsavingcontact', 'warning'); + +rcube_remote_response($commands); +exit; +?>
\ No newline at end of file diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc new file mode 100644 index 000000000..b7e91cb2f --- /dev/null +++ b/program/steps/mail/compose.inc @@ -0,0 +1,545 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/compose.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Compose a new mail message with all headers and attachments | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +require_once('Mail/mimeDecode.php'); + + +$MESSAGE_FORM = NULL; +$REPLY_MESSAGE = NULL; +$FORWARD_MESSAGE = NULL; + + +if (!is_array($_SESSION['compose'])) + $_SESSION['compose'] = array('id' => uniqid(rand())); + + +if ($_GET['_reply_uid'] || $_GET['_forward_uid']) + { + $msg_uid = $_GET['_reply_uid'] ? $_GET['_reply_uid'] : $_GET['_forward_uid']; + + // similar as in program/steps/mail/show.inc + $MESSAGE = array(); + $MESSAGE['headers'] = $IMAP->get_headers($msg_uid); + + $MESSAGE['source'] = rcmail_message_source($msg_uid); + + $mmd = new Mail_mimeDecode($MESSAGE['source']); + $MESSAGE['structure'] = $mmd->decode(array('include_bodies' => TRUE, + 'decode_headers' => TRUE, + 'decode_bodies' => FALSE)); + + $MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['headers']->subject); + $MESSAGE['parts'] = $mmd->getMimeNumbers($MESSAGE['structure']); + + if ($_GET['_reply_uid']) + { + $REPLY_MESSAGE = $MESSAGE; + $_SESSION['compose']['reply_uid'] = $_GET['_reply_uid']; + $_SESSION['compose']['reply_msgid'] = $REPLY_MESSAGE['headers']->messageID; + } + else + { + $FORWARD_MESSAGE = $MESSAGE; + $_SESSION['compose']['forward_uid'] = $_GET['_forward_uid']; + } + } + + + +/****** compose mode functions ********/ + + +function rcmail_compose_headers($attrib) + { + global $IMAP, $REPLY_MESSAGE, $DB; + + list($form_start, $form_end) = get_form_tags($attrib); + + $out = ''; + $part = strtolower($attrib['part']); + + switch ($part) + { + case 'from': + // 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'))) + $field_attrib[$attr] = $value; + + // get this user's identities + $sql_result = $DB->query(sprintf("SELECT identity_id, name, email + FROM %s + WHERE user_id=%d + AND del!='1' + ORDER BY `default` DESC, name ASC", + get_table_name('identities'), + $_SESSION['user_id'])); + + if ($DB->num_rows($sql_result)) + { + $select_from = new select($field_attrib); + while ($sql_arr = $DB->fetch_assoc($sql_result)) + $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $sql_arr['identity_id']); + + $out = $select_from->show($_POST['_from']); + } + else + { + $input_from = new textfield($field_attrib); + $out = $input_from->show($_POST['_from']); + } + + if ($form_start) + $out = $form_start.$out; + + return $out; + + + case 'to': + $fname = '_to'; + $header = 'to'; + + // we have contact id's as get parameters + if (strlen($_GET['_to']) && preg_match('/[0-9]+,?/', $_GET['_to'])) + { + $a_recipients = array(); + $sql_result = $DB->query(sprintf("SELECT name, email + FROM %s + WHERE user_id=%d + AND del!='1' + AND contact_id IN (%s)", + get_table_name('contacts'), + $_SESSION['user_id'], + $_GET['_to'])); + + 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 (strlen($_GET['_to'])) + $fvalue = $_GET['_to']; + + case 'cc': + if (!$fname) + { + $fname = '_cc'; + //$header = 'cc'; + } + case 'bcc': + if (!$fname) + $fname = '_bcc'; + + $allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'wrap'); + $field_type = 'textarea'; + break; + + case 'replyto': + case 'reply-to': + $fname = '_replyto'; + $allow_attrib = array('id', 'class', 'style', 'size'); + $field_type = 'textfield'; + break; + + } + + + if ($fname && $_POST[$fname]) + $fvalue = $_POST[$fname]; + else if ($header && is_object($REPLY_MESSAGE['headers'])) + { + // get recipent address(es) out of the message headers + if ($header=='to' && $REPLY_MESSAGE['headers']->replyto) + $fvalue = $IMAP->decode_header($REPLY_MESSAGE['headers']->replyto); + else if ($header=='to' && $REPLY_MESSAGE['headers']->from) + $fvalue = $IMAP->decode_header($REPLY_MESSAGE['headers']->from); + + // split recipients and put them back together in a unique way + $to_addresses = $IMAP->decode_address_list($fvalue); + $fvalue = ''; + foreach ($to_addresses as $addr_part) + $fvalue .= (strlen($fvalue) ? ', ':'').$addr_part['string']; + } + + + 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 rcube_compose_headers($attrib) + { + global $CONFIG, $OUTPUT; + + list($form_start, $form_end) = get_form_tags($attrib); + + // allow the following attributes to be added to the headers table + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border')); + + $labels = array(); + $labels['from'] = rcube_label('from'); + $labels['to'] = rcube_label('to'); + $labels['cc'] = rcube_label('cc'); + $labels['bcc'] = rcube_label('bcc'); + $labels['replyto'] = rcube_label('replyto'); + + $input_from = new textfield(array('name' => '_from', 'size' => 30)); + $input_to = new textfield(array('name' => '_to', 'size' => 30)); + $input_cc = new textfield(array('name' => '_cc', 'size' => 30)); + $input_bcc = new textfield(array('name' => '_bcc', 'size' => 30)); + $input_replyto = new textfield(array('name' => '_replyto', 'size' => 30)); + + $fields = array(); + $fields['from'] = $input_from->show($_POST['_from']); + $fields['to'] = $input_to->show($_POST['_to']); + $fields['cc'] = $input_cc->show($_POST['_cc']); + $fields['bcc'] = $input_bcc->show($_POST['_bcc']); + $fields['replyto'] = $input_replyto->show($_POST['_replyto']); + + + $out = <<<EOF +$form_start +<table$attrib_str><tr> + +<td class="title">$labels[from]</td> +<td>$fields[from]</td> + +</tr><tr> + +<td class="title">$labels[to]</td> +<td>$fields[to]</td> + +</tr><tr> + +<td class="title">$labels[cc]</td> +<td>$fields[cc]</td> + +</tr><tr> + +<td class="title">$labels[bcc]</td> +<td>$fields[bcc]</td> + +</tr><tr> + +<td class="title">$labels[replyto]</td> +<td>$fields[replyto]</td> + +</tr></table> +$form_end +EOF; + + return $out; + } +*/ + + +function rcmail_compose_body($attrib) + { + global $CONFIG, $REPLY_MESSAGE, $FORWARD_MESSAGE; + + list($form_start, $form_end) = get_form_tags($attrib); + unset($attrib['form']); + + $attrib['name'] = '_message'; + $textarea = new textarea($attrib); + + $body = ''; + + // use posted message body + if ($_POST['_message']) + $body = $_POST['_message']; + + // compose reply-body + else if (is_array($REPLY_MESSAGE['parts'])) + { + $body = rcmail_first_text_part($REPLY_MESSAGE['parts']); + if (strlen($body)) + $body = rcmail_create_reply_body($body); + } + + // forward message body inline + else if (is_array($FORWARD_MESSAGE['parts'])) + { + $body = rcmail_first_text_part($FORWARD_MESSAGE['parts']); + if (strlen($body)) + $body = rcmail_create_forward_body($body); + } + + $out = $form_start ? "$form_start\n" : ''; + $out .= $textarea->show($body); + $out .= $form_end ? "\n$form_end" : ''; + + return $out; + } + + +function rcmail_create_reply_body($body) + { + global $IMAP, $REPLY_MESSAGE; + + // 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 + $pefix = sprintf("\n\n\nOn %s, %s wrote:\n", + $REPLY_MESSAGE['headers']->date, + $IMAP->decode_header($REPLY_MESSAGE['headers']->from)); + + return $pefix.$body; + } + + +function rcmail_create_forward_body($body) + { + global $IMAP, $FORWARD_MESSAGE; + + // 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", + $FORWARD_MESSAGE['subject'], + $FORWARD_MESSAGE['headers']->date, + $IMAP->decode_header($FORWARD_MESSAGE['headers']->from), + $IMAP->decode_header($FORWARD_MESSAGE['headers']->to)); + + return $prefix.$body; + } + + + +function rcmail_compose_subject($attrib) + { + global $CONFIG, $REPLY_MESSAGE, $FORWARD_MESSAGE; + + 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 ($_POST['_subject']) + $subject = $_POST['_subject']; + + // create a reply-subject + else if (isset($REPLY_MESSAGE['subject'])) + $subject = 'Re: '.$REPLY_MESSAGE['subject']; + + // create a forward-subject + else if (isset($FORWARD_MESSAGE['subject'])) + $subject = 'Fwd: '.$FORWARD_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; + + // 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'])) + { + foreach ($_SESSION['compose']['attachments'] as $i => $a_prop) + $out .= sprintf("<li>%s</li>\n", $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(1, 2, 0, 4, 5)); + + $sel = $_POST['_priority'] ? $_POST['_priority'] : 0; + + $out = $form_start ? "$form_start\n" : ''; + $out .= $selector->show($sel); + $out .= $form_end ? "\n$form_end" : ''; + + return $out; + } + + +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 = !strlen($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 = strlen($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; + } + + +/****** get contacts for this user and add them to client scripts ********/ + +$sql_result = $DB->query(sprintf("SELECT name, email + FROM %s + WHERE user_id=%d + AND del!='1'", + get_table_name('contacts'), + $_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'], rep_specialchars_output($sql_arr['name'], 'js')); + + $OUTPUT->add_script(sprintf("$JS_OBJECT_NAME.set_env('contacts', %s);", array2js($a_contacts))); + } + + + + +parse_template('compose'); +?>
\ No newline at end of file diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc new file mode 100644 index 000000000..2109bed3a --- /dev/null +++ b/program/steps/mail/func.inc @@ -0,0 +1,1101 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/func.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | 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'; + +// set imap properties and session vars +if (strlen($_GET['_mbox'])) + { + $IMAP->set_mailbox($_GET['_mbox']); + $_SESSION['mbox'] = $_GET['_mbox']; + } + +if (strlen($_GET['_page'])) + { + $IMAP->set_page($_GET['_page']); + $_SESSION['page'] = $_GET['_page']; + } + + +// define url for getting message parts +if (strlen($_GET['_uid'])) + $GET_URL = sprintf('%s&_action=get&_mbox=%s&_uid=%d', $COMM_PATH, $IMAP->get_mailbox_name(), $_GET['_uid']); + + +// set current mailbox in client environment +$OUTPUT->add_script(sprintf("%s.set_env('mailbox', '%s');", $JS_OBJECT_NAME, $IMAP->get_mailbox_name())); + + +if ($CONFIG['trash_mbox']) + $OUTPUT->add_script(sprintf("%s.set_env('trash_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['trash_mbox'])); + + + +// return the mailboxlist in HTML +function rcmail_mailbox_list($attrib) + { + global $IMAP, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $COMM_PATH; + static $s_added_script = FALSE; + + $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 + $a_folders = $IMAP->list_mailboxes(); + $mbox = $IMAP->get_mailbox_name(); + + // for these mailboxes we have localized labels + $special_mailboxes = array('inbox', 'sent', 'drafts', 'trash', 'junk'); + + foreach ($a_folders as $i => $folder) + { + $zebra_class = $i%2 ? 'even' : 'odd'; + + $folder_lc = strtolower($folder); + if (in_array($folder_lc, $special_mailboxes)) + $foldername = rcube_label($folder_lc); + else + $foldername = $folder; + + if ($unread_count = $IMAP->messagecount($folder, 'UNSEEN')) + $foldername .= sprintf(' (%d)', $unread_count); + + // compose mailbox line + if ($type=='select') + $out .= sprintf('<option value="%s">%s</option>'."\n", + $folder, + rep_specialchars_output($foldername)); + + else + $out .= sprintf('<li class="mailbox %s %s%s%s"><a href="#%s" onclick="return %s.command(\'list\',\'%s\')" onmouseup="return %s.mbox_mouse_up(\'%s\')">%s</a></li>'."\n", + preg_replace('/[^a-z0-9\-_]/', '', $folder_lc), + $zebra_class, + $unread_count ? ' unread' : '', + $folder==$mbox ? ' selected' : '', + $folder, + $JS_OBJECT_NAME, + $folder, + $JS_OBJECT_NAME, + $folder, + rep_specialchars_output($foldername)); + } + + if ($type=='ul') + $OUTPUT->add_script(sprintf("%s.gui_object('mailboxlist', '%s');", $JS_OBJECT_NAME, $attrib['id'])); + + +/* this is added further up + if (!$s_added_script) + { + $javascript .= sprintf("%s.set_env('mailbox', '%s');", $JS_OBJECT_NAME, $mbox); + $OUTPUT->add_script($javascript); + $s_added_script = TRUE; + } +*/ + return $out . "</$type>"; + } + + +// return the message list as HTML table +function rcmail_message_list($attrib) + { + global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT, $JS_OBJECT_NAME; + + $skin_path = $CONFIG['skin_path']; + $image_tag = '<img src="%s%s" alt="%s" border="0" />'; + + // get message headers + $a_headers = $IMAP->list_headers(); + + // 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'); + + // show 'to' instead of from in sent messages + if (strtolower($IMAP->get_mailbox_name())=='sent' && ($f = array_search('from', $a_show_cols))) + $a_show_cols[$f] = 'to'; + + + // add table title + $out .= "<thead><tr>\n<td class=\"icon\"> </td>\n"; + + foreach ($a_show_cols as $col) + $out .= '<td class="'.$col.'">' . rcube_label($col) . "</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, + 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->seen) + $js_row_arr['unread'] = true; + if ($header->answered) + $js_row_arr['replied'] = true; + + // set message icon + 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\/m/i", $header->ctype)) + $attach_icon = $attrib['attachmenticon']; + + $out .= sprintf('<tr id="rcmrow%d" class="message'.($header->seen ? '' : ' unread').' '.$zebra_class.'">'."\n", $header->uid); + $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 = rep_specialchars_output(rcmail_address_string($header->$col, 3, $attrib['addicon'])); + else if ($col=='subject') + $cont = rep_specialchars_output($IMAP->decode_header($header->$col)); + else if ($col=='size') + $cont = show_bytes($header->$col); + else if ($col=='date') + $cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date)); + else + $cont = rep_specialchars_output($header->$col); + + $out .= '<td class="'.$col.'">' . $cont . "</td>\n"; + } + + $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : ''); + $out .= "</tr>\n"; + + if (sizeof($js_row_arr)) + $a_js_message_arr[$header->uid] = $js_row_arr; + } + + // complete message table + $out .= "</tbody></table>\n"; + + + $message_count = $IMAP->messagecount(); + + // set client env + $javascript = sprintf("%s.gui_object('messagelist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']); + $javascript .= sprintf("%s.set_env('messagecount', %d);\n", $JS_OBJECT_NAME, $message_count); + $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $IMAP->list_page); + $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($message_count/$IMAP->page_size)); + + if ($attrib['messageicon']) + $javascript .= sprintf("%s.set_env('messageicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['messageicon']); + if ($attrib['unreadicon']) + $javascript .= sprintf("%s.set_env('unreadicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['unreadicon']); + if ($attrib['repliedicon']) + $javascript .= sprintf("%s.set_env('repliedicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['repliedicon']); + if ($attrib['attachmenticon']) + $javascript .= sprintf("%s.set_env('attachmenticon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['attachmenticon']); + + $javascript .= sprintf("%s.set_env('messages', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr)); + + $OUTPUT->add_script($javascript); + + return $out; + } + + + + +// return javascript commands to add rows to the message list +function rcmail_js_message_list($a_headers, $insert_top=FALSE) + { + global $CONFIG, $IMAP; + + $commands = ''; + $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject'); + + // show 'to' instead of from in sent messages + if (strtolower($IMAP->get_mailbox_name())=='sent' && ($f = array_search('from', $a_show_cols))) + $a_show_cols[$f] = 'to'; + + // 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 = rep_specialchars_output(rcmail_address_string($header->$col, 3)); + else if ($col=='subject') + $cont = rep_specialchars_output($IMAP->decode_header($header->$col)); + else if ($col=='size') + $cont = show_bytes($header->$col); + else if ($col=='date') + $cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date)); + else + $cont = rep_specialchars_output($header->$col); + + $a_msg_cols[$col] = $cont; + } + + $a_msg_flags['unread'] = $header->seen ? 0 : 1; + $a_msg_flags['replied'] = $header->answered ? 1 : 0; + + $commands .= sprintf("this.add_message_row(%s, %s, %s, %b);\n", + $header->uid, + array2js($a_msg_cols), + array2js($a_msg_flags), + preg_match("/multipart\/m/i", $header->ctype)); + } + + return $commands; + } + + + +function rcmail_messagecount_display($attrib) + { + global $IMAP, $OUTPUT, $JS_OBJECT_NAME; + + if (!$attrib['id']) + $attrib['id'] = 'rcmcountdisplay'; + + $OUTPUT->add_script(sprintf("%s.gui_object('countdisplay', '%s');", $JS_OBJECT_NAME, $attrib['id'])); + + // allow the following attributes to be added to the <span> tag + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); + + + $out = '<span' . $attrib_str . '>'; + $out .= rcmail_get_messagecount_text(); + $out .= '</span>'; + return $out; + } + + + +function rcmail_get_messagecount_text() + { + global $IMAP, $MESSAGE; + + if (isset($MESSAGE['index'])) + { + $a_msg_index = $IMAP->message_index(); + return rcube_label(array('name' => 'messagenrof', + 'vars' => array('nr' => $MESSAGE['index']+1, + 'count' => sizeof($a_msg_index)))); + } + + $start_msg = ($IMAP->list_page-1) * $IMAP->page_size + 1; + $max = $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 $out; + } + + +function rcmail_print_body($part, $safe=FALSE, $plain=FALSE) // $body, $ctype_primary='text', $ctype_secondary='plain', $encoding='7bit', $safe=FALSE, $plain=FALSE) + { + global $IMAP, $REMOTE_OBJECTS, $JS_OBJECT_NAME; + + // extract part properties: body, ctype_primary, ctype_secondary, encoding, parameters + extract($part); + + $block = $plain ? '%s' : '%s'; //'<div style="display:block;">%s</div>'; + $body = $IMAP->mime_decode($body, $encoding); + $body = $IMAP->charset_decode($body, $parameters); + + + // text/html + if ($ctype_secondary=='html') + { + if (!$safe) // remove remote images and scripts + { + $remote_patterns = array('/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui', + // '/(src|background)=(["\']?)([\.\/]+[^"\'\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('\\1=\\2#\\4', + // '\\1=\\2#\\4', + '', + '\\1#\\3', + '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 sprintf($block, rep_specialchars_output($body, 'html', '', FALSE)); + } + + // text/enriched + if ($ctype_secondary=='enriched') + { + $body = enriched_to_html($body); + return sprintf($block, rep_specialchars_output($body, 'html')); + } + 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)"; + + $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); + + // colorize quoted parts + for($n=0; $n<sizeof($a_lines); $n++) + { + $line = $a_lines[$n]; + + if ($line{2}=='>') + $color = 'red'; + else if ($line{1}=='>') + $color = 'green'; + else if ($line{0}=='>') + $color = 'blue'; + else + $color = FALSE; + + $line = rep_specialchars_output($line, 'html', 'replace', FALSE); + + if ($color) + $a_lines[$n] = sprintf('<font color="%s">%s</font>', $color, $line); + else + $a_lines[$n] = $line; + } + + // insert the links for urls and mailtos + $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines)); + + return sprintf($block, "<pre>\n".$body."\n</pre>"); + } + } + + + +// 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'])) + $a_return_parts[] = array('type' => 'headers', + 'headers' => $structure->headers); + + // print body if message doesn't have multiple parts + if ($message_ctype_primary=='text') + { + $a_return_parts[] = array('type' => 'content', + 'body' => $structure->body, + 'ctype_primary' => $message_ctype_primary, + 'ctype_secondary' => $message_ctype_secondary, + 'encoding' => $structure->headers['content-transfer-encoding']); + } + + // 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)) + $a_return_parts[] = array('type' => 'content', + 'body' => $print_part->body, + 'ctype_primary' => strtolower($print_part->ctype_primary), + 'ctype_secondary' => strtolower($print_part->ctype_secondary), + 'parameters' => $print_part->ctype_parameters, + 'encoding' => $print_part->headers['content-transfer-encoding']); + // show plaintext warning + else if ($html_part!==NULL) + $a_return_parts[] = array('type' => 'content', + 'body' => rcube_label('htmlmessage'), + 'ctype_primary' => 'text', + 'ctype_secondary' => 'plain'); + + // add html part as attachment + if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part) + { + $html_part = $structure->parts[$html_part]; + $a_attachments[] = array('filename' => rcube_label('htmlmessage'), + 'encoding' => $html_part->headers['content-transfer-encoding'], + 'mimetype' => 'text/html', + 'part_id' => $html_part->mime_id, + 'size' => strlen($IMAP->mime_decode($html_part->body, $html_part->headers['content-transfer-encoding']))); + } + } + + // message contains multiple parts + else if ($message_ctype_primary=='multipart' && is_array($structure->parts)) + { + foreach ($structure->parts as $mail_part) + { + $primary_type = strtolower($mail_part->ctype_primary); + $secondary_type = strtolower($mail_part->ctype_secondary); + + // multipart/alternative + if ($primary_type=='multipart') // && ($secondary_type=='alternative' || $secondary_type=='mixed' || $secondary_type=='related')) + { + 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')) || + ($primary_type=='message' && $secondary_type=='delivery-status')) + { + $a_return_parts[] = array('type' => 'content', + 'body' => $mail_part->body, + 'ctype_primary' => $primary_type, + 'ctype_secondary' => $secondary_type, + 'encoding' => $mail_part->headers['content-transfer-encoding']); + } + + // part message/* + else if ($primary_type=='message') + { + /* don't parse headers here; they're parsed within the recursive call to rcmail_parse_message() + if ($mail_part->parts[0]->headers) + $a_return_parts[] = array('type' => 'headers', + 'headers' => $mail_part->parts[0]->headers); + */ + + list($parts, $attachmnts) = rcmail_parse_message($mail_part->parts[0], $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']) + { + if ($message_ctype_secondary=='related' && $mail_part->headers['content-id']) + $sa_inline_objects[] = array('filename' => $mail_part->d_parameters['filename'], + 'mimetype' => strtolower("$primary_type/$secondary_type"), + 'part_id' => $mail_part->mime_id, + 'content_id' => preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id'])); + + else if ($mail_part->d_parameters['filename']) + $a_attachments[] = array('filename' => $mail_part->d_parameters['filename'], + 'encoding' => strtolower($mail_part->headers['content-transfer-encoding']), + 'mimetype' => strtolower("$primary_type/$secondary_type"), + 'part_id' => $mail_part->mime_id, + 'size' => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*, + 'content' => $mail_part->body */); + + else if ($mail_part->ctype_parameters['name']) + $a_attachments[] = array('filename' => $mail_part->ctype_parameters['name'], + 'encoding' => strtolower($mail_part->headers['content-transfer-encoding']), + 'mimetype' => strtolower("$primary_type/$secondary_type"), + 'part_id' => $mail_part->mime_id, + 'size' => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*, + 'content' => $mail_part->body */); + + + } + } + + + // if this was a related part try to resolve references + if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects)) + { + $a_replace_patters = array(); + $a_replace_strings = array(); + + foreach ($sa_inline_objects as $inline_object) + { + $a_replace_patters[] = 'cid:'.$inline_object['content_id']; + $a_replace_strings[] = sprintf($get_url, $inline_object['part_id']); + } + + foreach ($a_return_parts as $i => $return_part) + { + if ($return_part['type']!='content') + continue; + + // decode body and replace cid:... + $a_return_parts[$i]['body'] = str_replace($a_replace_patters, $a_replace_strings, $IMAP->mime_decode($return_part['body'], $return_part['encoding'])); + $a_return_parts[$i]['encoding'] = '7bit'; + } + } + } + + + // join all parts together + //$out .= join($part_delimiter, $a_return_parts); + + 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', 'reply-to', 'date'); + + foreach ($standard_headers as $hkey) + { + if (!$headers[$hkey]) + continue; + + if ($hkey=='date') + $header_value = format_date(strtotime($headers[$hkey])); + else if (in_array($hkey, array('from', 'to', 'cc', 'reply-to'))) + $header_value = rep_specialchars_output(rcmail_address_string($IMAP->decode_header($headers[$hkey]), NULL, $attrib['addicon'])); + else + $header_value = rep_specialchars_output($IMAP->decode_header($headers[$hkey]), '', 'all'); + + $out .= "\n<tr>\n"; + $out .= '<td class="header-title">'.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, $GET_URL, $REMOTE_OBJECTS, $JS_OBJECT_NAME; + + if (!is_array($MESSAGE['parts']) && !$MESSAGE['body']) + return ''; + + if (!$attrib['id']) + $attrib['id'] = 'rcmailMsgBody'; + + $safe_mode = (bool)$_GET['_safe']; + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); + $out = '<div '. $attrib_str . ">\n"; + + $header_attrib = array(); + foreach ($attrib as $attr => $value) + if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs)) + $header_attrib[$regs[1]] = $value; + + + // this is an ecrypted message + // -> create a plaintext body with the according message + if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted') + { + $MESSAGE['parts'][0] = array('type' => 'content', + 'ctype_primary' => 'text', + 'ctype_secondary' => 'plain', + 'body' => rcube_label('encryptedmessage')); + } + + 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') + { +// var_dump($part['parameters']); + // $body = rcmail_print_body($part['body'], $part['ctype_primary'], $part['ctype_secondary'], $part['encoding'], $safe_mode); + $body = rcmail_print_body($part, $safe_mode); + $out .= '<div class="message-part">'; + $out .= rcmail_mod_html_body($body, $attrib['id']); + $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", + $GET_URL, $attach_prop['part_id'], + $attach_prop['filename'], + $attach_prop['filename']); + } + } + + // tell client that there are blocked remote objects + if ($REMOTE_OBJECTS && !$safe_mode) + $OUTPUT->add_script(sprintf("%s.set_env('blockedobjects', true);", $JS_OBJECT_NAME)); + + $out .= "\n</div>"; + return $out; + } + + + +// modify a HTML message that it can be displayed inside a HTML page +function rcmail_mod_html_body($body, $container_id) + { + $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))) + { + $pos2 += 8; + $body_pre = substr($body, 0, $pos); + $styles = substr($body, $pos, $pos2-$pos); + $body_post = substr($body, $pos2, strlen($body)-$pos2); + + // replace all css definitions with #container [def] + $styles = rcmail_mod_css_styles($styles, $container_id); + + $body = $body_pre . $styles . $body_post; + $last_style_pos = $pos2; + } + + + // remove SCRIPT tags + while (($pos = strpos($body_lc, '<script')) && ($pos2 = strpos($body_lc, '</script>', $pos))) + { + $pos2 += 8; + $body = substr($body, 0, $pos) . substr($body, $pos2, strlen($body)-$pos2); + $body_lc = strtolower($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); + } + + + // add comments arround html and other tags + $out = preg_replace(array('/(<\/?html[^>]*>)/i', + '/(<\/?head[^>]*>)/i', + '/(<title[^>]*>.+<\/title>)/ui', + '/(<\/?meta[^>]*>)/i'), + '<!--\\1-->', + $body); + + $out = preg_replace(array('/(<body[^>]*>)/i', + '/(<\/body>)/i'), + array('<div class="rcmBody">', + '</div>'), + $out); + + + return $out; + } + + + +// 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; + } + + $styles = preg_replace('/(^\s*|,\s*)([a-z0-9\._][a-z0-9\.\-_]*)/im', "\\1#$container_id \\2", $source); + $styles = preg_replace('/<<str_replacement\[([0-9]+)\]>>/e', "\$a_css_values[\\1]", $styles); + + // replace body definition because we also stripped off the <body> tag + $styles = preg_replace("/$container_id\s+body/i", "$container_id div.rcmBody", $styles); + + return $styles; + } + + + +// return first text part of a message +function rcmail_first_text_part($message_parts) + { + if (!is_array($message_parts)) + return FALSE; + + $html_part = NULL; + + // check all message parts + foreach ($message_parts as $pid => $part) + { + $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary); + if ($mimetype=='text/plain') + { + $body = rcube_imap::mime_decode($part->body, $part->headers['content-transfer-encoding']); + $body = rcube_imap::charset_decode($body, $part->ctype_parameters); + return $body; + } + else if ($mimetype=='text/html') + { + $html_part = rcube_imap::mime_decode($part->body, $part->headers['content-transfer-encoding']); + $html_part = rcube_imap::charset_decode($html_part, $part->ctype_parameters); + } + } + + + // convert HTML to plain text + if ($html_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; + } + + +// get source code of a specific message and cache it +function rcmail_message_source($uid) + { + global $IMAP, $DB; + + // get message ID if uid is given + $headers = $IMAP->get_headers($uid); + $message_id = $headers->messageID; + + // get cached message source + $msg_source = rcube_read_cache($message_id); + + // get message from server and cache it + if (!$msg_source) + { + $msg_source = $IMAP->get_raw_body($uid); + rcube_write_cache($message_id, $msg_source, TRUE); + } + + return $msg_source; + } + + +// decode address string and re-format it as HTML links +function rcmail_address_string($input, $max=NULL, $addicon=NULL) + { + global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $EMAIL_ADDRESS_PATTERN; + + $a_parts = $IMAP->decode_address_list($input); + + if (!sizeof($a_parts)) + return $input; + + $c = count($a_parts); + $j = 0; + $out = ''; + + foreach ($a_parts as $part) + { + $j++; + if ($PRINT_MODE) + $out .= sprintf('%s <%s>', htmlentities($part['name']), $part['mailto']); + else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto'])) + { + $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>', + $part['mailto'], + $JS_OBJECT_NAME, + $part['mailto'], + $part['mailto'], + htmlentities($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 .= htmlentities($part['name']); + if ($part['mailto']) + $out .= (strlen($out) ? ' ' : '') . sprintf('<%s>', $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"; + + $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name']; + $filesize = strlen($IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding'])); + + if ($filename) + { + $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n", + rcube_label('filename'), + rep_specialchars_output($filename), + str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), + rcube_label('download')); + } + + if ($filesize) + $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n", + rcube_label('filesize'), + show_bytes($filesize)); + + $out .= "\n</table>"; + + return $out; + } + + + +function rcmail_message_part_frame($attrib) + { + global $MESSAGE; + + $part = $MESSAGE['parts'][$_GET['_part']]; + $ctype_primary = strtolower($part->ctype_primary); + + $attrib['src'] = './?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']); + + $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height')); + $out = '<iframe '. $attrib_str . "></ifame>"; + + 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']); + + // kill temp dir + if ($_SESSION['compose']['temp_dir']) + rmdir($_SESSION['compose']['temp_dir']); + + unset($_SESSION['compose']); + } + + +?>
\ No newline at end of file diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc new file mode 100644 index 000000000..018ae2e4c --- /dev/null +++ b/program/steps/mail/get.inc @@ -0,0 +1,159 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/get.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Delivering a specific part of a mail message | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +require_once('Mail/mimeDecode.php'); + + +// show loading page +if ($_GET['_preload']) + { + $url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']); + $message = rcube_label('loadingdata'); + + print "<html>\n<head>\n" . + '<meta http-equiv="refresh" content="0; url='.$url.'">' . + "\n</head>\n<body>" . + $message . + "\n</body>\n</html>"; + exit; + } + + + +// similar code as in program/steps/mail/show.inc +if ($_GET['_uid']) + { + $MESSAGE = array(); + $MESSAGE['source'] = rcmail_message_source($_GET['_uid']); + + $mmd = new Mail_mimeDecode($MESSAGE['source']); + $MESSAGE['structure'] = $mmd->decode(array('include_bodies' => TRUE, + 'decode_headers' => FALSE, + 'decode_bodies' => FALSE)); + + $MESSAGE['parts'] = $mmd->getMimeNumbers($MESSAGE['structure']); + } + + + +// show part page +if ($_GET['_frame']) + { + parse_template('messagepart'); + exit; + } + +else if ($_GET['_part']) + { + if ($part = $MESSAGE['parts'][$_GET['_part']]); + { + $ctype_primary = strtolower($part->ctype_primary); + $ctype_secondary = strtolower($part->ctype_secondary); + + $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary); + $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name']; + + if ($ctype_primary=='text') + { + list($MESSAGE['parts']) = rcmail_parse_message($MESSAGE['structure'], + array('safe' => (bool)$_GET['_safe'], + 'prefer_html' => TRUE, + 'get_url' => $GET_URL.'&_part=%s')); + + $cont = rcmail_print_body($MESSAGE['parts'][0], (bool)$_GET['_safe']); + } + else + $cont = $IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding']); + + // send correct headers for content type and length + if ($_GET['_download']) + { + // send download headers + header("Content-Type: application/octet-stream"); + header(sprintf('Content-Disposition: attachment; filename="%s"', + $filename ? $filename : "roundcube.$ctype_secondary")); + } + else + { + header("Content-Type: $mimetype"); + header(sprintf('Content-Disposition: inline; filename="%s"', $filename)); + } + + header(sprintf('Content-Length: %d', strlen($cont))); + + // deliver part content + echo $cont; + exit; + } + } + +// print message +else + { + $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary); + $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary); + $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary); + + // send correct headers for content type + header("Content-Type: text/html"); + + $cont = ''; + list($MESSAGE['parts']) = rcmail_parse_message($MESSAGE['structure'], + array('safe' => (bool)$_GET['_safe'], + 'get_url' => $GET_URL.'&_part=%s')); + + if ($MESSAGE['parts'] && $ctype_primary=='multipart') + { + // reset output page + $OUTPUT = new rcube_html_page(); + parse_template('messagepart'); + exit; + } + else if ($MESSAGE['parts'][0]) + { + $part = $MESSAGE['parts'][0]; + $cont = rcmail_print_body($part, (bool)$_GET['_safe']); + } + else + $cont = $IMAP->get_body($_GET['_uid']); + + $OUTPUT = new rcube_html_page(); + $OUTPUT->write($cont); + +/* + if ($mimetype=='text/html') + print $cont; + else + { + print "<html>\n<body>\n"; + print $cont; + print "\n</body>\n</html>"; + } +*/ + exit; + } + + +// if we arrive here, the requested part was not found +header('HTTP/1.1 404 Not Found'); +exit; + +?>
\ No newline at end of file diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc new file mode 100644 index 000000000..58a1bb84e --- /dev/null +++ b/program/steps/mail/list.inc @@ -0,0 +1,49 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/list.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Send message list to client (as remote response) | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = TRUE; + +$count = $IMAP->messagecount(); + +// update message count display +$pages = ceil($count/$IMAP->page_size); +$commands = sprintf("this.set_env('messagecount', %d);\n", $count); +$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages); +$commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text()); + +// update mailboxlist +$mbox = $IMAP->get_mailbox_name(); +$commands .= sprintf("this.set_unread_count('%s', %d);\n", $mbox, $IMAP->messagecount($mbox, 'UNSEEN')); + + +// add message rows +if ($count) + { + $a_headers = $IMAP->list_headers($mbox); + $commands .= rcmail_js_message_list($a_headers); + } + + +// send response +rcube_remote_response($commands); + +exit; +?>
\ No newline at end of file diff --git a/program/steps/mail/mark.inc b/program/steps/mail/mark.inc new file mode 100644 index 000000000..2dadb53eb --- /dev/null +++ b/program/steps/mail/mark.inc @@ -0,0 +1,41 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/mark.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Mark the submitted messages with the specified flag | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = TRUE; + +$a_flags_map = array('read' => 'SEEN', + 'unread' => 'UNSEEN'); + +if ($_GET['_uid'] && $_GET['_flag']) + { + $flag = $a_flags_map[$_GET['_flag']] ? $a_flags_map[$_GET['_flag']] : strtoupper($_GET['_flag']); + $marked = $IMAP->set_flag($_GET['_uid'], $flag); + + if ($marked) + { + $mbox = $IMAP->get_mailbox_name(); + $commands = sprintf("this.set_unread_count('%s', %d);\n", $mbox, $IMAP->messagecount($mbox, 'UNSEEN')); + rcube_remote_response($commands); + } + } + +exit; +?>
\ No newline at end of file diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc new file mode 100644 index 000000000..e090d305b --- /dev/null +++ b/program/steps/mail/move_del.inc @@ -0,0 +1,82 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/move_del.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Move the submitted messages to a specific mailbox or delete them | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = TRUE; + +// move messages +if ($_action=='moveto' && $_GET['_uid'] && $_GET['_target_mbox']) + { + $count = sizeof(explode(',', $_GET['_uid'])); + $moved = $IMAP->move_message($_GET['_uid'], $_GET['_target_mbox'], $_GET['_mbox']); + + if (!$moved) + { + // send error message + exit; + } + } + +// delete messages +else if ($_action=='delete' && $_GET['_uid']) + { + $count = sizeof(explode(',', $_GET['_uid'])); + $del = $IMAP->delete_message($_GET['_uid'], $_GET['_mbox']); + + if (!$del) + { + // send error message + exit; + } + } + +// unknown action or missing query param +else + { + exit; + } + + +// update message count display +$pages = ceil($IMAP->messagecount()/$IMAP->page_size); +$commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text()); +$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages); + + +// update mailboxlist +$mbox = $IMAP->get_mailbox_name(); +$commands .= sprintf("this.set_unread_count('%s', %d);\n", $mbox, $IMAP->messagecount($mbox, 'UNSEEN')); +$commands .= sprintf("this.set_unread_count('%s', %d);\n", $_GET['_target_mbox'], $IMAP->messagecount($_GET['_target_mbox'], 'UNSEEN')); + + +// add new rows from next page (if any) +if ($_GET['_from']!='show' && $pages>1 && $IMAP->list_page < $pages) + { + $a_headers = $IMAP->list_headers($mbox); + $a_headers = array_slice($a_headers, -$count, $count); + $commands .= rcmail_js_message_list($a_headers); + } + + +// send response +rcube_remote_response($commands); + +exit; +?>
\ No newline at end of file diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc new file mode 100644 index 000000000..789001e14 --- /dev/null +++ b/program/steps/mail/sendmail.inc @@ -0,0 +1,251 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/sendmail.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Compose a new mail message with all headers and attachments | + | and send it using IlohaMail's SMTP methods or with PHP mail() | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +require_once('lib/smtp.inc'); +require_once('Mail/mime.php'); + + +if (!isset($_SESSION['compose']['id'])) + { + $_action = 'list'; + return; + } + + +/****** message sending functions ********/ + + + +function rcmail_get_identity($id) + { + global $DB; + + // get identity record + $sql_result = $DB->query(sprintf("SELECT *, email AS mailto + FROM %s + WHERE identity_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('identities'), + $id, + $_SESSION['user_id'])); + + if ($DB->num_rows($sql_result)) + { + $sql_arr = $DB->fetch_assoc($sql_result); + $out = $sql_arr; + $out['string'] = sprintf('%s <%s>', $sql_arr['name'], $sql_arr['mailto']); + return $out; + } + + return FALSE; + } + + + +/****** check submission and compose message ********/ + + +$mailto_regexp = '/,\s*$/'; + +// trip ending ', ' from +$mailto = preg_replace($mailto_regexp, '', $_POST['_to']); + +// decode address strings +$to_address_arr = $IMAP->decode_address_list($mailto); +$identity_arr = rcmail_get_identity($_POST['_from']); + + +$from = $identity_arr['mailto']; +$first_to = is_array($to_address_arr[0]) ? $to_address_arr[0]['mailto'] : $mailto; + + +// create unique message-id +$message_id = sprintf('<%s@%s>', md5(uniqid('rcmail')), $_SESSION['imap_host']); + + +// compose headers array +$headers = array('Date' => date('D, j M Y G:i:s O'), + 'From' => $identity_arr['string'], + 'To' => $mailto); + +// additional recipients +if ($_POST['_cc']) + $headers['Cc'] = preg_replace($mailto_regexp, '', $_POST['_cc']); + +if ($_POST['_bcc']) + $headers['Bcc'] = preg_replace($mailto_regexp, '', $_POST['_bcc']); + +if (strlen($identity_arr['bcc'])) + $headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc']; + +// add subject +$headers['Subject'] = trim(stripslashes($_POST['_subject'])); + +if (strlen($identity_arr['organization'])) + $headers['Organization'] = $identity_arr['organization']; + +if (strlen($identity_arr['reply-to'])) + $headers['Reply-To'] = $identity_arr['reply-to']; + +if ($_SESSION['compose']['reply_msgid']) + $headers['In-Reply-To'] = $_SESSION['compose']['reply_msgid']; + + +if ($_POST['_priority']) + { + $priority = (int)$_POST['_priority']; + $a_priorities = array(1=>'lowest', 2=>'low', 4=>'high', 5=>'highest'); + if ($str_priority = $a_priorities[$priority]) + $headers['X-Priority'] = sprintf("%d (%s)", $priority, ucfirst($str_priority)); + } + + +// additional headers +$headers['Message-ID'] = $message_id; +$headers['X-Sender'] = $from; + +if ($CONFIG['useragent']) + $headers['User-Agent'] = $CONFIG['useragent']; + + +// create PEAR::Mail_mime instance +$MAIL_MIME = new Mail_mime(); +$MAIL_MIME->setTXTBody(stripslashes($_POST['_message']), FALSE, TRUE); +//$MAIL_MIME->setTXTBody(wordwrap(stripslashes($_POST['_message'])), FALSE, TRUE); + + +// add stored attachments, if any +if (is_array($_SESSION['compose']['attachments'])) + foreach ($_SESSION['compose']['attachments'] as $attachment) + $MAIL_MIME->addAttachment($attachment['path'], $attachment['mimetype'], $attachment['name'], TRUE); + + +// add submitted attachments +if (is_array($_FILES['_attachments']['tmp_name'])) + foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) + $MAIL_MIME->addAttachment($filepath, $files['type'][$i], $files['name'][$i], TRUE); + + +// compose message body and get headers +$msg_body = $MAIL_MIME->get(); +$msg_subject = $headers['Subject']; + + +// send thru SMTP server using cusotm SMTP library +if ($CONFIG['smtp_server']) + { + // connect to SMTP server + $smtp_conn = smtp_connect($CONFIG['smtp_server'], '25', $CONFIG['smtp_user'], $CONFIG['smtp_pass']); + + if ($smtp_conn) + { + // generate list of recipients + $recipients = $mailto.', '.$headers['Cc'].', '.$headers['Bcc']; + $a_recipients = smtp_expand($recipients); + + // generate message headers + $header_str = $MAIL_MIME->txtHeaders($headers); + + // send message + $sent = smtp_mail($smtp_conn, $from, $a_recipients, $header_str."\r\n".$msg_body, FALSE); + } + + // log error + if (!$smtp_conn || !$sent) + { + raise_error(array('code' => 800, + 'type' => 'smtp', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Connection failed: $smtp_error"), TRUE, FALSE); + } + } + +// send mail using PHP's mail() function +else + { + // unset some headers because they will be added by the mail() function + $headers_php = $headers; + unset($headers_php['To'], $headers_php['Subject']); + + $header_str = $MAIL_MIME->txtHeaders($headers_php); + $sent = mail($mailto, $msg_subject, $msg_body, $header_str, "-f$from"); + } + + +// return to compose page if sending failed +if (!$sent) + { + $_action = 'compose'; + $OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action)); + show_message("sendingfailed", 'error'); + return; + } + + +// set repliead flag +if ($_SESSION['compose']['reply_uid']) + $IMAP->set_flag($_SESSION['compose']['reply_uid'], 'ANSWERED'); + + +// copy message to sent folder +if ($CONFIG['sent_mbox']) + { + // create string of complete message headers + $header_str = $MAIL_MIME->txtHeaders($headers); + + // check if mailbox exists + if (!in_array_nocase($CONFIG['sent_mbox'], $IMAP->list_mailboxes())) + $IMAP->create_mailbox($CONFIG['sent_mbox'], TRUE); + + // append message to sent box + $saved = $IMAP->save_message($CONFIG['sent_mbox'], $header_str."\r\n".$msg_body); + } + + +// log mail sending +if ($CONFIG['smtp_log']) + { + $log_entry = sprintf("[%s] User: %d; Message for %s; Subject: %s\n", + date("d-M-Y H:i:s O", mktime()), + $_SESSION['user_id'], + $mailto, + $msg_subject); + + if ($fp = fopen($INSTALL_PATH.'logs/sendmail', 'a')) + { + fwrite($fp, $log_entry); + fclose($fp); + } + } + + +// show confirmation +show_message('messagesent', 'confirmation'); + + +// kill compose entry from session +rcmail_compose_cleanup(); + +?>
\ No newline at end of file diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc new file mode 100644 index 000000000..de4f2b4cb --- /dev/null +++ b/program/steps/mail/show.inc @@ -0,0 +1,169 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/show.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Display a mail message similar as a usual mail application does | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +require_once('Mail/mimeDecode.php'); + +$PRINT_MODE = $_action=='print' ? TRUE : FALSE; + + +// similar code as in program/steps/mail/get.inc +if ($_GET['_uid']) + { + $MESSAGE = array(); + $MESSAGE['headers'] = $IMAP->get_headers($_GET['_uid']); + $MESSAGE['source'] = rcmail_message_source($_GET['_uid']); + + // go back to list if message not found (wrong UID) + if (!$MESSAGE['headers'] || !$MESSAGE['source']) + { + $_action = 'list'; + return; + } + + $mmd = new Mail_mimeDecode($MESSAGE['source']); + $MESSAGE['structure'] = $mmd->decode(array('include_bodies' => TRUE, + 'decode_headers' => FALSE, + 'decode_bodies' => FALSE)); + + $mmd->getMimeNumbers($MESSAGE['structure']); + + $MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['structure']->headers['subject']); + + if ($MESSAGE['structure']) + list($MESSAGE['parts'], $MESSAGE['attachments']) = rcmail_parse_message($MESSAGE['structure'], + array('safe' => (bool)$_GET['_safe'], + 'prefer_html' => $CONFIG['prefer_html'], + 'get_url' => $GET_URL.'&_part=%s')); + else + $MESSAGE['body'] = $IMAP->get_body($_GET['_uid']); + + + // mark message as read + if (!$MESSAGE['headers']->seen) + $IMAP->set_flag($_GET['_uid'], 'SEEN'); + + // give message uid to the client + $javascript = sprintf("%s.set_env('uid', '%s');\n", $JS_OBJECT_NAME, $_GET['_uid']); + $javascript .= sprintf("%s.set_env('safemode', '%b');", $JS_OBJECT_NAME, $_GET['_safe']); + + // get previous and next message UID + $a_msg_index = $IMAP->message_index(); + $MESSAGE['index'] = array_search((string)$_GET['_uid'], $a_msg_index, TRUE); + + if (isset($a_msg_index[$MESSAGE['index']-1])) + $javascript .= sprintf("\n%s.set_env('prev_uid', '%s');", $JS_OBJECT_NAME, $a_msg_index[$MESSAGE['index']-1]); + if (isset($a_msg_index[$MESSAGE['index']+1])) + $javascript .= sprintf("\n%s.set_env('next_uid', '%s');", $JS_OBJECT_NAME, $a_msg_index[$MESSAGE['index']+1]); + + $OUTPUT->add_script($javascript); + } + + + +function rcmail_message_attachments($attrib) + { + global $CONFIG, $OUTPUT, $PRINT_MODE, $MESSAGE, $GET_URL, $JS_OBJECT_NAME; + + if (sizeof($MESSAGE['attachments'])) + { + // allow the following attributes to be added to the <ul> tag + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); + $out = '<ul' . $attrib_str . ">\n"; + + foreach ($MESSAGE['attachments'] as $attach_prop) + { + if ($PRINT_MODE) + $out .= sprintf('<li>%s (%s)</li>'."\n", + $attach_prop['filename'], + show_bytes($attach_prop['size'])); + else + $out .= sprintf('<li><a href="#attachment" onclick="return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)">%s</a></li>'."\n", + $JS_OBJECT_NAME, + $attach_prop['part_id'], + $attach_prop['mimetype'], + $attach_prop['filename']); + /* direct link + else + $out .= sprintf('<li><a href="%s&_part=%s&_frame=1" target="rcubemailattachment">%s</a></li>'."\n", + $GET_URL, + $attach_prop['part_id'], + $attach_prop['filename']); + */ + } + + $out .= "</ul>"; + return $out; + } + } + + + +// return an HTML iframe for loading mail content +function rcmail_messagecontent_frame($attrib) + { + global $COMM_PATH, $OUTPUT, $GET_URL, $JS_OBJECT_NAME; + + // allow the following attributes to be added to the <iframe> tag + $attrib_str = create_attrib_string($attrib); + $framename = 'rcmailcontentwindow'; + + $out = sprintf('<iframe src="%s" name="%s"%s>%s</iframe>'."\n", + $GET_URL, + $framename, + $attrib_str, + rcube_label('loading')); + + + $OUTPUT->add_script("$JS_OBJECT_NAME.set_env('contentframe', '$framename');"); + + return $out; + } + + +function rcmail_remote_objects_msg($attrib) + { + global $CONFIG, $OUTPUT, $JS_OBJECT_NAME; + + if (!$attrib['id']) + $attrib['id'] = 'rcmremoteobjmsg'; + + // allow the following attributes to be added to the <div> tag + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); + $out = '<div' . $attrib_str . ">"; + + $out .= rep_specialchars_output(sprintf('%s <a href="#loadimages" onclick="%s.command(\'load-images\')" title="%s">%s</a>', + rcube_label('blockedimages'), + $JS_OBJECT_NAME, + rcube_label('showimages'), + rcube_label('showimages'))); + + $out .= '</div>'; + + $OUTPUT->add_script(sprintf("%s.gui_object('remoteobjectsmsg', '%s');", $JS_OBJECT_NAME, $attrib['id'])); + return $out; + } + + +if ($_action=='print') + parse_template('printmessage'); +else + parse_template('message'); +?>
\ No newline at end of file diff --git a/program/steps/mail/upload.inc b/program/steps/mail/upload.inc new file mode 100644 index 000000000..4f1eb3dab --- /dev/null +++ b/program/steps/mail/upload.inc @@ -0,0 +1,75 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/upload.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Handle file-upload and make them available as attachments | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +if (!$_SESSION['compose']) + { + exit; + } + + +if (strlen($CONFIG['temp_dir'])) + $temp_dir = $CONFIG['temp_dir'].(!eregi('\/$', $CONFIG['temp_dir']) ? '/' : '').$_SESSION['compose']['id']; + +if (!is_array($_SESSION['compose']['attachments'])) + { + $_SESSION['compose']['attachments'] = array(); + + // create temp-dir for uploaded attachments + if ($CONFIG['temp_dir'] && is_writeable($CONFIG['temp_dir'])) + { + mkdir($temp_dir); + $_SESSION['compose']['temp_dir'] = $temp_dir; + } + } + + +$response = ''; + +foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) + { + $tmpfname = tempnam($temp_dir, 'rcmAttmnt'); + if (copy($filepath, $tmpfname)) + { + $_SESSION['compose']['attachments'][] = array('name' => $_FILES['_attachments']['name'][$i], + 'mimetype' => $_FILES['_attachments']['type'][$i], + 'path' => $tmpfname); + + $response .= sprintf("parent.%s.add2attachment_list('%s');\n", $JS_OBJECT_NAME, $_FILES['_attachments']['name'][$i]); + } + } + + +// send html page with JS calls as response +print <<<EOF +<html> +<script type="text/javascript"> +if (parent.$JS_OBJECT_NAME) +{ +$response +parent.$JS_OBJECT_NAME.show_attachment_form(false); +} +</script> +</html> +EOF; +exit; + +?>
\ No newline at end of file diff --git a/program/steps/mail/viewsource.inc b/program/steps/mail/viewsource.inc new file mode 100644 index 000000000..8b8c90cd1 --- /dev/null +++ b/program/steps/mail/viewsource.inc @@ -0,0 +1,39 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/viewsource.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Display a mail message similar as a usual mail application does | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +// similar code as in program/steps/mail/get.inc +if ($_GET['_uid']) + { + header('Content-Type: text/plain'); + print rcmail_message_source($_GET['_uid']); + } +else + { + raise_error(array('code' => 500, + 'type' => 'php', + 'message' => 'Message UID '.$_GET['_uid'].' not found'), + TRUE, + TRUE); + } + +exit; +?>
\ No newline at end of file diff --git a/program/steps/settings/delete_identity.inc b/program/steps/settings/delete_identity.inc new file mode 100644 index 000000000..dacfc0563 --- /dev/null +++ b/program/steps/settings/delete_identity.inc @@ -0,0 +1,55 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/delete_identity.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Delete the submitted identities (IIDs) from the database | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$REMOTE_REQUEST = $_GET['_remote'] ? TRUE : FALSE; + +if ($_GET['_iid']) + { + $DB->query(sprintf("UPDATE %s + SET del='1' + WHERE user_id=%d + AND identity_id IN (%s)", + get_table_name('identities'), + $_SESSION['user_id'], + $_GET['_iid'])); + + $count = $DB->affected_rows(); + if ($count) + { + $commands = show_message('deletedsuccessfully', 'confirmation'); + } + + // send response + if ($REMOTE_REQUEST) + rcube_remote_response($commands); + } + + +if ($REMOTE_REQUEST) + exit; + + +// go to identities page +$_action = 'identities'; + +// overwrite action variable +$OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action)); +?>
\ No newline at end of file diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc new file mode 100644 index 000000000..f4134d329 --- /dev/null +++ b/program/steps/settings/edit_identity.inc @@ -0,0 +1,106 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/edit_identity.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Show edit form for a identity record or to add a new one | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +if (($_GET['_iid'] || $_POST['_iid']) && $_action=='edit-identity') + { + $id = $_POST['_iid'] ? $_POST['_iid'] : $_GET['_iid']; + $DB->query(sprintf("SELECT * FROM %s + WHERE identity_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('identities'), + $id, + $_SESSION['user_id'])); + + $IDENTITY_RECORD = $DB->fetch_assoc(); + + if (is_array($IDENTITY_RECORD)) + $OUTPUT->add_script(sprintf("%s.set_env('iid', '%s');", $JS_OBJECT_NAME, $IDENTITY_RECORD['identity_id'])); + + $PAGE_TITLE = rcube_label('edititem'); + } +else + $PAGE_TITLE = rcube_label('newitem'); + + + +function rcube_identity_form($attrib) + { + global $IDENTITY_RECORD, $JS_OBJECT_NAME; + + if (!$IDENTITY_RECORD && $GLOBALS['_action']!='add-identity') + return rcube_label('notfound'); + + list($form_start, $form_end) = get_form_tags($attrib, 'save-identity', array('name' => '_iid', 'value' => $IDENTITY_RECORD['identity_id'])); + unset($attrib['form']); + + + // list of available cols + $a_show_cols = array('name' => array('type' => 'text'), + 'email' => array('type' => 'text'), + 'organization' => array('type' => 'text'), + 'reply-to' => array('type' => 'text', 'label' => 'replyto'), + 'bcc' => array('type' => 'text'), + 'default' => array('type' => 'checkbox', 'label' => 'setdefault')); + + + // a specific part is requested + if ($attrib['part']) + { + $colprop = $a_show_cols[$attrib['part']]; + if (is_array($colprop)) + { + $out = $form_start; + $out .= rcmail_get_edit_field($attrib['part'], $IDENTITY_RECORD[$attrib['part']], $attrib, $colprop['type']); + return $out; + } + else + return ''; + } + + + // return the complete edit form as table + $out = "$form_start<table>\n\n"; + + foreach ($a_show_cols as $col => $colprop) + { + $attrib['id'] = 'rcmfd_'.$col; + $label = strlen($colprop['label']) ? $colprop['label'] : $col; + $value = rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $attrib, $colprop['type']); + + $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", + $attrib['id'], + rcube_label($label), + $value); + } + + $out .= "\n</table>$form_end"; + + return $out; + } + + + +if ($_action=='add-identity' && template_exists('addidentity')) + parse_template('addidentity'); + +parse_template('editidentity'); +?>
\ No newline at end of file diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc new file mode 100644 index 000000000..826717fd9 --- /dev/null +++ b/program/steps/settings/func.inc @@ -0,0 +1,194 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/func.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide functionality for user's settings & preferences | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +// get user record +$sql_result = $DB->query(sprintf("SELECT username, mail_host FROM %s + WHERE user_id=%d", + get_table_name('users'), + $_SESSION['user_id'])); + +if ($USER_DATA = $DB->fetch_assoc($sql_result)) + $PAGE_TITLE = sprintf('%s %s@%s', rcube_label('settingsfor'), $USER_DATA['username'], $USER_DATA['mail_host']); + + + +function rcmail_user_prefs_form($attrib) + { + global $DB, $CONFIG, $sess_user_lang; + + list($form_start, $form_end) = get_form_tags($attrib, 'save-prefs'); + unset($attrib['form']); + + // 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')); + + // return the complete edit form as table + $out = "$form_start<table" . $attrib_str . ">\n\n"; + + $a_show_cols = array('language' => array('type' => 'text'), + 'pagesize' => array('type' => 'text'), + 'timezone' => array('type' => 'text')); + + // show language selection + $field_id = 'rcmfd_lang'; + $select_lang = new select(array('name' => '_language', 'id' => $field_id)); + $select_lang->add('English', 'en'); + $select_lang->add('Deutsch', 'de'); + + $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", + $field_id, + rcube_label('language'), + $select_lang->show($sess_user_lang)); + + + // show page size selection + $field_id = 'rcmfd_timezone'; + $select_timezone = new select(array('name' => '_timezone', 'id' => $field_id)); + $select_timezone->add('(GMT -11:00) Midway Island, Samoa', '-11'); + $select_timezone->add('(GMT -10:00) Hawaii', '-10'); + $select_timezone->add('(GMT -9:00) Alaska', '-9'); + $select_timezone->add('(GMT -8:00) Pacific Time (US/Canada)', '-8'); + $select_timezone->add('(GMT -7:00) Mountain Time (US/Canada)', '-7'); + $select_timezone->add('(GMT -6:00) Central Time (US/Canada), Mexico City', '-6'); + $select_timezone->add('(GMT -5:00) Eastern Time (US/Canada), Bogota, Lima', '-5'); + $select_timezone->add('(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz', '-4'); + $select_timezone->add('(GMT -3:00) Brazil, Buenos Aires, Georgetown', '-3'); + $select_timezone->add('(GMT -2:00) Mid-Atlantic', '-2'); + $select_timezone->add('(GMT -1:00) Azores, Cape Verde Islands', '-1'); + $select_timezone->add('(GMT) Western Europe, London, Lisbon, Casablanca', '0'); + $select_timezone->add('(GMT +1:00) Central European Time', '1'); + $select_timezone->add('(GMT +2:00) EET: Kaliningrad, South Africa', '2'); + $select_timezone->add('(GMT +3:00) Baghdad, Kuwait, Riyadh, Moscow, Nairobi', '3'); + $select_timezone->add('(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi', '4'); + $select_timezone->add('(GMT +5:00) Ekaterinburg, Islamabad, Karachi', '5'); + $select_timezone->add('(GMT +6:00) Almaty, Dhaka, Colombo', '6'); + $select_timezone->add('(GMT +7:00) Bangkok, Hanoi, Jakarta', '7'); + $select_timezone->add('(GMT +8:00) Beijing, Perth, Singapore, Taipei', '8'); + $select_timezone->add('(GMT +9:00) Tokyo, Seoul, Yakutsk', '9'); + $select_timezone->add('(GMT +10:00) EAST/AEST: Guam, Vladivostok', '10'); + $select_timezone->add('(GMT +11:00) Magadan, Solomon Islands', '11'); + $select_timezone->add('(GMT +12:00) Auckland, Wellington, Kamchatka', '12'); + $select_timezone->add('(GMT +13:00) Tonga, Pheonix Islands', '13'); + $select_timezone->add('(GMT +14:00) Kiribati', '14'); + + + $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", + $field_id, + rcube_label('timezone'), + $select_timezone->show($CONFIG['timezone'])); + + + // show page size selection + $field_id = 'rcmfd_pgsize'; + $input_pagesize = new textfield(array('name' => '_pagesize', 'id' => $field_id, 'size' => 5)); + + $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", + $field_id, + rcube_label('pagesize'), + $input_pagesize->show($CONFIG['pagesize'])); + + // show checkbox for HTML/plaintext messages + $field_id = 'rcmfd_htmlmsg'; + $input_pagesize = new checkbox(array('name' => '_prefer_html', 'id' => $field_id, 'value' => 1)); + + $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n", + $field_id, + rcube_label('preferhtml'), + $input_pagesize->show($CONFIG['prefer_html']?1:0)); + + + $out .= "\n</table>$form_end"; + + return $out; + } + + + + +function rcmail_identities_list($attrib) + { + global $DB, $CONFIG, $OUTPUT, $JS_OBJECT_NAME; + + + // get contacts from DB + $sql_result = $DB->query(sprintf("SELECT * FROM %s + WHERE del!='1' + AND user_id=%d + ORDER BY `default` DESC, name ASC", + get_table_name('identities'), + $_SESSION['user_id'])); + + + // add id to message list table if not specified + if (!strlen($attrib['id'])) + $attrib['id'] = 'rcmIdentitiesList'; + + // define list of cols to be displayed + $a_show_cols = array('name', 'email', 'organization', 'reply-to'); + + // create XHTML table + $out = rcube_table_output($attrib, $sql_result, $a_show_cols, 'identity_id'); + + // set client env + $javascript = sprintf("%s.gui_object('identitieslist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']); + $OUTPUT->add_script($javascript); + + return $out; + } + + + +// similar function as in /steps/addressbook/edit.inc +function get_form_tags($attrib, $action, $add_hidden=array()) + { + global $OUTPUT, $JS_OBJECT_NAME, $EDIT_FORM, $SESS_HIDDEN_FIELD; + + $form_start = ''; + if (!strlen($EDIT_FORM)) + { + $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task'])); + $hiddenfields->add(array('name' => '_action', 'value' => $action)); + + if ($add_hidden) + $hiddenfields->add($add_hidden); + + if ($_GET['_framed'] || $_POST['_framed']) + $hiddenfields->add(array('name' => '_framed', 'value' => 1)); + + $form_start = !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : ''; + $form_start .= "\n$SESS_HIDDEN_FIELD\n"; + $form_start .= $hiddenfields->show(); + } + + $form_end = (!strlen($EDIT_FORM) && !strlen($attrib['form'])) ? '</form>' : ''; + $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form'; + + if (!strlen($EDIT_FORM)) + $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('editform', '$form_name');"); + + $EDIT_FORM = $form_name; + + return array($form_start, $form_end); + } + + +?>
\ No newline at end of file diff --git a/program/steps/settings/identities.inc b/program/steps/settings/identities.inc new file mode 100644 index 000000000..b760f09bf --- /dev/null +++ b/program/steps/settings/identities.inc @@ -0,0 +1,48 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/identities.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Manage identities of a user account | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +if ($USER_DATA = $DB->fetch_assoc($sql_result)) + $PAGE_TITLE = sprintf('%s (%s@%s)', rcube_label('identities'), $USER_DATA['username'], $USER_DATA['mail_host']); + + + +// similar function as /steps/addressbook/func.inc::rcmail_contact_frame() +function rcmail_identity_frame($attrib) + { + global $OUTPUT, $JS_OBJECT_NAME; + + if (!$attrib['id']) + $attrib['id'] = 'rcmIdentityFrame'; + + $attrib['name'] = $attrib['id']; + + $OUTPUT->add_script(sprintf("%s.set_env('contentframe', '%s');", $JS_OBJECT_NAME, $attrib['name'])); + + $attrib_str = create_attrib_string($attrib, array('name', 'id', 'class', 'style', 'src', 'width', 'height', 'frameborder')); + $out = '<iframe'. $attrib_str . '></iframe>'; + + return $out; + } + + + +parse_template('identities'); +?>
\ No newline at end of file diff --git a/program/steps/settings/manage_folders.inc b/program/steps/settings/manage_folders.inc new file mode 100644 index 000000000..38f9e1a0e --- /dev/null +++ b/program/steps/settings/manage_folders.inc @@ -0,0 +1,176 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/manage_folders.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Provide functionality to create/delete/rename folders | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +// init IAMP connection +rcmail_imap_init(TRUE); + + +// subscribe to one or more mailboxes +if ($_action=='subscribe') + { + if (strlen($_GET['_mboxes'])) + $IMAP->subscribe(explode(',', $_GET['_mboxes'])); + + if ($_GET['_remote']) + rcube_remote_response('// subscribed'); + } + +// unsubscribe one or more mailboxes +else if ($_action=='unsubscribe') + { + if (strlen($_GET['_mboxes'])) + $IMAP->unsubscribe(explode(',', $_GET['_mboxes'])); + + if ($_GET['_remote']) + rcube_remote_response('// unsubscribed'); + } + +// create a new mailbox +else if ($_action=='create-folder') + { + if (strlen($_GET['_name'])) + $create = $IMAP->create_mailbox(trim($_GET['_name']), TRUE); + + if ($create && $_GET['_remote']) + { + $commands = sprintf("this.add_folder_row('%s')", rep_specialchars_output($_GET['_name'], 'js')); + rcube_remote_response($commands); + } + else if (!$create && $_GET['_remote']) + { + $commands = show_message('errorsaving', 'error'); + rcube_remote_response($commands); + } + else if (!$create) + show_message('errorsaving', 'error'); + } + +// delete an existing IMAP mailbox +else if ($_action=='delete-folder') + { + if (strlen($_GET['_mboxes'])) + $IMAP->delete_mailbox(explode(',', $_GET['_mboxes'])); + + if ($_GET['_remote']) + rcube_remote_response('// deleted'); + } + + + +// build table with all folders listed by server +function rcube_subscription_form($attrib) + { + global $IMAP, $CONFIG, $OUTPUT, $JS_OBJECT_NAME; + + list($form_start, $form_end) = get_form_tags($attrib, 'folders'); + unset($attrib['form']); + + + if (!$attrib['id']) + $attrib['id'] = 'rcmSubscriptionlist'; + + // 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 = "$form_start\n<table" . $attrib_str . ">\n"; + + + // add table header + $out .= "<thead><tr>\n"; + $out .= sprintf('<td>%s</td><td>%s</td><td></td>', rcube_label('foldername'), rcube_label('subscribed')); + $out .= "\n</tr></thead>\n<tbody>\n"; + + + // get folders from server + $a_unsubscribed = $IMAP->list_unsubscribed(); + $a_subscribed = $IMAP->list_mailboxes(); + $a_js_folders = array(); + + $checkbox_subscribe = new checkbox(array('name' => '_subscribed[]', 'onclick' => "$JS_OBJECT_NAME.command(this.checked?'subscribe':'unsubscribe',this.value)")); + + if ($attrib['deleteicon']) + $button = sprintf('<img src="%s%s" alt="%s" border="0" />', $CONFIG['skin_path'], $attrib['deleteicon'], rcube_label('delete')); + else + $button = rcube_label('delete'); + + + // create list of available folders + foreach ($a_unsubscribed as $i => $folder) + { + $zebra_class = $i%2 ? 'even' : 'odd'; + $folder_js = rep_specialchars_output($folder, 'js'); + $a_js_folders['rcmrow'.($i+1)] = $folder_js; + + $out .= sprintf('<tr id="rcmrow%d" class="%s"><td>%s</td><td>%s</td><td><a href="#delete" onclick="%s.command(\'delete-folder\',\'%s\')" title="%s">%s</a></td>', + $i+1, + $zebra_class, + rep_specialchars_output($folder, 'html'), + $checkbox_subscribe->show(in_array($folder, $a_subscribed)?$folder:'', array('value' => $folder)), + $JS_OBJECT_NAME, + $folder_js, + rcube_label('deletefolder'), + $button); + + $out .= "</tr>\n"; + } + + $out .= "</tbody>\n</table>"; + $out .= "\n$form_end"; + + + $javascript = sprintf("%s.gui_object('subscriptionlist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']); + $javascript .= sprintf("%s.set_env('subscriptionrows', %s);", $JS_OBJECT_NAME, array2js($a_js_folders)); + $OUTPUT->add_script($javascript); + + return $out; + } + + +function rcube_create_folder_form($attrib) + { + global $JS_OBJECT_NAME; + + list($form_start, $form_end) = get_form_tags($attrib, 'create-folder'); + unset($attrib['form']); + + + // return the complete edit form as table + $out = "$form_start\n"; + + $input = new textfield(array('name' => '_folder_name')); + $out .= $input->show(); + + if (get_boolean($attrib['button'])) + { + $button = new input_field(array('type' => 'button', + 'value' => rcube_label('create'), + 'onclick' => "$JS_OBJECT_NAME.command('create-folder',this.form)")); + $out .= $button->show(); + } + + $out .= "\n$form_end"; + + return $out; + } + + +parse_template('managefolders'); +?>
\ No newline at end of file diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc new file mode 100644 index 000000000..b4b1fec27 --- /dev/null +++ b/program/steps/settings/save_identity.inc @@ -0,0 +1,136 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/save_identity.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Save an identity record or to add a new one | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'default'); + + +// update an existing contact +if ($_POST['_iid']) + { + $a_write_sql = array(); + + foreach ($a_save_cols as $col) + { + $fname = '_'.$col; + if (!isset($_POST[$fname])) + continue; + + $a_write_sql[] = sprintf("`%s`='%s'", $col, addslashes($_POST[$fname])); + } + + if (sizeof($a_write_sql)) + { + $DB->query(sprintf("UPDATE %s + SET %s + WHERE identity_id=%d + AND user_id=%d + AND del!='1'", + get_table_name('identities'), + join(', ', $a_write_sql), + $_POST['_iid'], + $_SESSION['user_id'])); + + $updated = $DB->affected_rows(); + } + + if ($updated) + { + show_message('successfullysaved', 'confirmation'); + + // mark all other identities as 'not-default' + $DB->query(sprintf("UPDATE %s + SET `default`='0' + WHERE identity_id!=%d + AND user_id=%d + AND del!='1'", + get_table_name('identities'), + $_POST['_iid'], + $_SESSION['user_id'])); + + if ($_POST['_framed']) + { + // update the changed col in list + // ... + } + } + else + { + // show error message + + } + } + +// insert a new contact +else + { + $a_insert_cols = $a_insert_values = array(); + + foreach ($a_save_cols as $col) + { + $fname = '_'.$col; + if (!isset($_POST[$fname])) + continue; + + $a_insert_cols[] = "`$col`"; + $a_insert_values[] = sprintf("'%s'", addslashes($_POST[$fname])); + } + + if (sizeof($a_insert_cols)) + { + $DB->query(sprintf("INSERT INTO %s + (user_id, %s) + VALUES (%d, %s)", + get_table_name('identities'), + join(', ', $a_insert_cols), + $_SESSION['user_id'], + join(', ', $a_insert_values))); + + $insert_id = $DB->insert_id(); + } + + if ($insert_id) + { + $_GET['_iid'] = $insert_id; + + if ($_POST['_framed']) + { + // add contact row or jump to the page where it should appear + // .... + } + } + else + { + // show error message + } + } + + +// go to next step +if ($_POST['_framed']) + $_action = 'edit-identitiy'; +else + $_action = 'identities'; + + +// overwrite action variable +$OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action)); + +?>
\ No newline at end of file diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc new file mode 100644 index 000000000..1524b9ead --- /dev/null +++ b/program/steps/settings/save_prefs.inc @@ -0,0 +1,59 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/settings/save_prefs.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | All rights reserved. | + | | + | PURPOSE: | + | Save user preferences to DB and to the current session | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +$a_user_prefs = $_SESSION['user_prefs']; +if (!is_array($a_user_prefs)) + $a_user_prefs = array(); + + +$a_user_prefs['timezone'] = isset($_POST['_timezone']) ? (int)$_POST['_timezone'] : $CONFIG['timezone']; +$a_user_prefs['pagesize'] = is_numeric($_POST['_pagesize']) ? (int)$_POST['_pagesize'] : $CONFIG['pagesize']; +$a_user_prefs['prefer_html'] = isset($_POST['_prefer_html']) ? TRUE : FALSE; + +if (isset($_POST['_language'])) + $sess_user_lang = $_SESSION['user_lang'] = $_POST['_language']; + + +$DB->query(sprintf("UPDATE %s + SET preferences='%s', + language='%s' + WHERE user_id=%d", + get_table_name('users'), + addslashes(serialize($a_user_prefs)), + $sess_user_lang, + $_SESSION['user_id'])); + +if ($DB->affected_rows()) + { + show_message('successfullysaved', 'confirmation'); + + $_SESSION['user_prefs'] = $a_user_prefs; + $CONFIG = array_merge($CONFIG, $a_user_prefs); + } + + +// go to next step +$_action = 'preferences'; + +// overwrite action variable +$OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action)); + +?>
\ No newline at end of file diff --git a/skins/default/addresses.css b/skins/default/addresses.css new file mode 100644 index 000000000..d660c3c8f --- /dev/null +++ b/skins/default/addresses.css @@ -0,0 +1,119 @@ +/***** RoundCube|Mail address book task styles *****/ + + +#abooktoolbar +{ + position: absolute; + top: 32px; + left: 200px; + height: 35px; +} + +#abooktoolbar a +{ + padding-right: 10px; +} + +#abookcountbar +{ + position: absolute; + top: 50px; + left: 490px; + width: 200px; + height: 20px; + text-align: left; +} + +#abookcountbar span +{ + font-size: 11px; + color: #333333; +} + + +#addresslist +{ + position: absolute; + top: 75px; + left: 20px; + width: 450px; + bottom: 60px; + border: 1px solid #999999; + background-color: #F9F9F9; + overflow: auto; + /* css hack for IE */ + height: expression((parseInt(document.documentElement.clientHeight)-135)+'px'); +} + +#contacts-table +{ + width: 100%; + table-layout: fixed; + /* css hack for IE */ + width: expression(document.getElementById('addresslist').clientWidth); +} + + +#contacts-table tbody td +{ + cursor: pointer; +} + + +#contacts-box +{ + position: absolute; + top: 75px; + left: 490px; + right: 40px; + bottom: 60px; + border: 1px solid #999999; + overflow: hidden; + /* css hack for IE */ + width: expression((parseInt(document.documentElement.clientWidth)-530)+'px'); + height: expression((parseInt(document.documentElement.clientHeight)-135)+'px'); +} + +body.iframe, +#contact-frame +{ + background-color: #F9F9F9; +} + +#contact-frame +{ + border: none; +/* visibility: hidden; */ +} + +#contact-title +{ + height: 12px !important; +/* height: 20px; */ + padding: 4px 20px 3px 20px; + border-bottom: 1px solid #999999; + color: #333333; + font-size: 11px; + font-weight: bold; + background-color: #EBEBEB; + background-image: url(images/listheader_aqua.gif); +} + +#contact-details +{ + padding: 15px 20px 10px 20px; +} + +#contact-details table td.title +{ + color: #666666; + font-weight: bold; + text-align: right; + padding-right: 10px; +} + + + + + + diff --git a/skins/default/common.css b/skins/default/common.css new file mode 100755 index 000000000..2ba97d843 --- /dev/null +++ b/skins/default/common.css @@ -0,0 +1,277 @@ +/***** RoundCube|Mail basic styles *****/ + +body +{ + margin: 8px; + background-color: #F2F2F2; /* #EBEBEB; */ + color: #000000; +} + +body.iframe +{ + margin: 0px; +} + +body.extwin +{ + margin: 10px; +} + +body, td, th, span, div, p, h3 +{ + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + font-size: 12px; + color: #000000; +} + +th +{ + font-weight: normal; +} + +h3 +{ + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + font-size: 18px; + color: #000000; +} + +a, a:active, a:visited +{ + color: #000000; +} + +a.button, a.button:visited, a.tab, a.tab:visited, a.axislist +{ + color: #000000; + text-decoration: none; +} + +a.tab +{ + width: 80px; + display: block; + text-align: center; +} + +hr +{ + height: 1px; + background-color: #666666; + border-style: none; +} + +input, textarea +{ + font-size: 9pt; + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + padding: 1px; + padding-left: 3px; + padding-right: 3px; + background-color: #ffffff; + border: 1px solid #666666; +} + +input.button +{ + height: 20px; + color: #333333; + font-size: 12px; + padding-left: 8px; + padding-right: 8px; + background: url(images/buttons/bg.gif) repeat-x #f0f0f0; + border: 1px solid #a4a4a4; +} + +input.button:hover +{ + color: black; +} + +img +{ + behavior: url('skins/default/pngbehavior.htc'); +} + +.alttext +{ + font-size: 11px; +} + + +/** common user interface objects */ + +#header +{ +/* margin: 10px auto; */ + width: 170px; + height: 40px; + margin-top: 0px; + margin-left: 10px; +/* border: 1px solid #cccccc; */ +} + +#footer +{ + position: fixed !important; + left: 0px; + right: 0px; + bottom: 0px !important; + height: 40px; + background-color: #f2f2f2; + + /* css hack for IE */ + position: absolute; + bottom: auto; + top: expression((parseInt(document.documentElement.clientHeight)+parseInt(document.documentElement.scrollTop)-42)+'px'); + width: expression(parseInt(document.documentElement.clientWidth)+'px'); +} + +#taskbar +{ + margin: 0px auto; + width: 400px; + height: 34px; + padding: 3px; + text-align: center; + border: 1px solid #cccccc; +} + +#taskbar a +{ + padding-right: 10px; +} + + +#message +{ + position: absolute; + display: none; + top: 0px; + left: 200px; + right: 200px; + z-index: 5000; +} + +#message div +{ + width: 400px; + margin: 0px auto; + height: 22px; + min-height: 22px; + padding: 8px 10px 8px 46px; +} + +#message div.notice, +#remote-objects-message +{ + background: url(images/display/info.png) 6px 3px no-repeat; + background-color: #F7FDCB; + border: 1px solid #C2D071; +} + +#message div.error, +#message div.warning +{ + background: url(images/display/warning.png) 6px 3px no-repeat; + background-color: #EF9398; + border: 1px solid #DC5757; +} + +#message div.confirmation +{ + background: url(images/display/confirm.png) 6px 3px no-repeat; + background-color: #A6EF7B; + border: 1px solid #76C83F; +} + +#message div.loading +{ + background: url(images/display/loading.gif) 6px 3px no-repeat; + background-color: #EFEFEF; + border: 1px solid #CCCCCC; +} + + +/***** common table settings ******/ + +table.records-table thead tr td +{ + height: 20px; + padding: 0px 4px 0px 4px; + vertical-align: middle; + border-bottom: 1px solid #999999; + color: #333333; + background-color: #EBEBEB; + background-image: url(images/listheader_aqua.gif); + font-size: 11px; + font-weight: bold; +} + +table.records-table tbody tr td +{ + height: 16px; + padding: 2px 4px 2px 4px; + font-size: 11px; + white-space: nowrap; + border-bottom: 1px solid #EBEBEB; + overflow: hidden; + text-align: left; +} + +table.records-table tr +{ + background-color: #FFFFFF; +} + +table.records-table tr.selected td +{ + font-weight: bold; + color: #FFFFFF; + background-color: #CC3333; +} + + + +/***** roundcube webmail pre-defined classes *****/ + +a.rcmContactAddress +{ + text-decoration: none; +} + +a.rcmContactAddress:hover +{ + text-decoration: underline; +} + +#rcmKSearchpane +{ + background-color: #F9F9F9; + border: 1px solid #CCCCCC; +} + +#rcmKSearchpane ul +{ + margin: 0px; + padding: 2px; + list-style-image: none; + list-style-type: none; +} + +#rcmKSearchpane ul li +{ + height: 16px; + font-size: 11px; + padding-left: 8px; + padding-top: 2px; + padding-right: 8px; + white-space: nowrap; +} + +#rcmKSearchpane ul li.selected +{ + color: #ffffff; + background-color: #CC3333; +} + diff --git a/skins/default/images/blank.gif b/skins/default/images/blank.gif Binary files differnew file mode 100644 index 000000000..ea83374c1 --- /dev/null +++ b/skins/default/images/blank.gif diff --git a/skins/default/images/buttons/add_act.png b/skins/default/images/buttons/add_act.png Binary files differnew file mode 100644 index 000000000..0454ff856 --- /dev/null +++ b/skins/default/images/buttons/add_act.png diff --git a/skins/default/images/buttons/add_contact_act.png b/skins/default/images/buttons/add_contact_act.png Binary files differnew file mode 100644 index 000000000..994242c0a --- /dev/null +++ b/skins/default/images/buttons/add_contact_act.png diff --git a/skins/default/images/buttons/add_contact_pas.png b/skins/default/images/buttons/add_contact_pas.png Binary files differnew file mode 100644 index 000000000..91ca0d08a --- /dev/null +++ b/skins/default/images/buttons/add_contact_pas.png diff --git a/skins/default/images/buttons/add_pas.png b/skins/default/images/buttons/add_pas.png Binary files differnew file mode 100644 index 000000000..cf4de2e0b --- /dev/null +++ b/skins/default/images/buttons/add_pas.png diff --git a/skins/default/images/buttons/addressbook.png b/skins/default/images/buttons/addressbook.png Binary files differnew file mode 100644 index 000000000..359f33e0f --- /dev/null +++ b/skins/default/images/buttons/addressbook.png diff --git a/skins/default/images/buttons/attach_act.png b/skins/default/images/buttons/attach_act.png Binary files differnew file mode 100644 index 000000000..612d36d19 --- /dev/null +++ b/skins/default/images/buttons/attach_act.png diff --git a/skins/default/images/buttons/attach_pas.png b/skins/default/images/buttons/attach_pas.png Binary files differnew file mode 100644 index 000000000..37c67c9c3 --- /dev/null +++ b/skins/default/images/buttons/attach_pas.png diff --git a/skins/default/images/buttons/back_act.png b/skins/default/images/buttons/back_act.png Binary files differnew file mode 100644 index 000000000..d5352b5b3 --- /dev/null +++ b/skins/default/images/buttons/back_act.png diff --git a/skins/default/images/buttons/back_pas.png b/skins/default/images/buttons/back_pas.png Binary files differnew file mode 100644 index 000000000..ac15bade5 --- /dev/null +++ b/skins/default/images/buttons/back_pas.png diff --git a/skins/default/images/buttons/bg.gif b/skins/default/images/buttons/bg.gif Binary files differnew file mode 100644 index 000000000..e2191c910 --- /dev/null +++ b/skins/default/images/buttons/bg.gif diff --git a/skins/default/images/buttons/compose_act.png b/skins/default/images/buttons/compose_act.png Binary files differnew file mode 100644 index 000000000..c7e2d61d5 --- /dev/null +++ b/skins/default/images/buttons/compose_act.png diff --git a/skins/default/images/buttons/compose_pas.png b/skins/default/images/buttons/compose_pas.png Binary files differnew file mode 100644 index 000000000..5fd9d7ab2 --- /dev/null +++ b/skins/default/images/buttons/compose_pas.png diff --git a/skins/default/images/buttons/contacts_act.png b/skins/default/images/buttons/contacts_act.png Binary files differnew file mode 100644 index 000000000..987612817 --- /dev/null +++ b/skins/default/images/buttons/contacts_act.png diff --git a/skins/default/images/buttons/contacts_pas.png b/skins/default/images/buttons/contacts_pas.png Binary files differnew file mode 100644 index 000000000..25dfd8c32 --- /dev/null +++ b/skins/default/images/buttons/contacts_pas.png diff --git a/skins/default/images/buttons/delete_act.png b/skins/default/images/buttons/delete_act.png Binary files differnew file mode 100644 index 000000000..b141cd3a6 --- /dev/null +++ b/skins/default/images/buttons/delete_act.png diff --git a/skins/default/images/buttons/delete_pas.png b/skins/default/images/buttons/delete_pas.png Binary files differnew file mode 100644 index 000000000..fc1e892a4 --- /dev/null +++ b/skins/default/images/buttons/delete_pas.png diff --git a/skins/default/images/buttons/download_act.png b/skins/default/images/buttons/download_act.png Binary files differnew file mode 100644 index 000000000..694527de3 --- /dev/null +++ b/skins/default/images/buttons/download_act.png diff --git a/skins/default/images/buttons/download_pas.png b/skins/default/images/buttons/download_pas.png Binary files differnew file mode 100644 index 000000000..fb39db5dd --- /dev/null +++ b/skins/default/images/buttons/download_pas.png diff --git a/skins/default/images/buttons/edit_contact_act.png b/skins/default/images/buttons/edit_contact_act.png Binary files differnew file mode 100644 index 000000000..57b278278 --- /dev/null +++ b/skins/default/images/buttons/edit_contact_act.png diff --git a/skins/default/images/buttons/edit_contact_pas.png b/skins/default/images/buttons/edit_contact_pas.png Binary files differnew file mode 100644 index 000000000..b999294c8 --- /dev/null +++ b/skins/default/images/buttons/edit_contact_pas.png diff --git a/skins/default/images/buttons/forward_act.png b/skins/default/images/buttons/forward_act.png Binary files differnew file mode 100644 index 000000000..2fdbdbad0 --- /dev/null +++ b/skins/default/images/buttons/forward_act.png diff --git a/skins/default/images/buttons/forward_pas.png b/skins/default/images/buttons/forward_pas.png Binary files differnew file mode 100644 index 000000000..e671398eb --- /dev/null +++ b/skins/default/images/buttons/forward_pas.png diff --git a/skins/default/images/buttons/inbox_act.png b/skins/default/images/buttons/inbox_act.png Binary files differnew file mode 100644 index 000000000..30c1e7635 --- /dev/null +++ b/skins/default/images/buttons/inbox_act.png diff --git a/skins/default/images/buttons/inbox_pas.png b/skins/default/images/buttons/inbox_pas.png Binary files differnew file mode 100644 index 000000000..67f4da08d --- /dev/null +++ b/skins/default/images/buttons/inbox_pas.png diff --git a/skins/default/images/buttons/logout.gif b/skins/default/images/buttons/logout.gif Binary files differnew file mode 100644 index 000000000..f8000fc5a --- /dev/null +++ b/skins/default/images/buttons/logout.gif diff --git a/skins/default/images/buttons/logout.png b/skins/default/images/buttons/logout.png Binary files differnew file mode 100644 index 000000000..2fe632ab3 --- /dev/null +++ b/skins/default/images/buttons/logout.png diff --git a/skins/default/images/buttons/mail.png b/skins/default/images/buttons/mail.png Binary files differnew file mode 100644 index 000000000..30c1e7635 --- /dev/null +++ b/skins/default/images/buttons/mail.png diff --git a/skins/default/images/buttons/next_act.png b/skins/default/images/buttons/next_act.png Binary files differnew file mode 100644 index 000000000..fed82945c --- /dev/null +++ b/skins/default/images/buttons/next_act.png diff --git a/skins/default/images/buttons/next_pas.png b/skins/default/images/buttons/next_pas.png Binary files differnew file mode 100644 index 000000000..df80ad344 --- /dev/null +++ b/skins/default/images/buttons/next_pas.png diff --git a/skins/default/images/buttons/previous_act.png b/skins/default/images/buttons/previous_act.png Binary files differnew file mode 100644 index 000000000..457d873c5 --- /dev/null +++ b/skins/default/images/buttons/previous_act.png diff --git a/skins/default/images/buttons/previous_pas.png b/skins/default/images/buttons/previous_pas.png Binary files differnew file mode 100644 index 000000000..db7186d56 --- /dev/null +++ b/skins/default/images/buttons/previous_pas.png diff --git a/skins/default/images/buttons/print_act.png b/skins/default/images/buttons/print_act.png Binary files differnew file mode 100644 index 000000000..19e1f3341 --- /dev/null +++ b/skins/default/images/buttons/print_act.png diff --git a/skins/default/images/buttons/print_pas.png b/skins/default/images/buttons/print_pas.png Binary files differnew file mode 100644 index 000000000..b6c0e7838 --- /dev/null +++ b/skins/default/images/buttons/print_pas.png diff --git a/skins/default/images/buttons/reply_act.png b/skins/default/images/buttons/reply_act.png Binary files differnew file mode 100644 index 000000000..89ad7cc9b --- /dev/null +++ b/skins/default/images/buttons/reply_act.png diff --git a/skins/default/images/buttons/reply_pas.png b/skins/default/images/buttons/reply_pas.png Binary files differnew file mode 100644 index 000000000..0b389337e --- /dev/null +++ b/skins/default/images/buttons/reply_pas.png diff --git a/skins/default/images/buttons/send_act.png b/skins/default/images/buttons/send_act.png Binary files differnew file mode 100644 index 000000000..999d21d5d --- /dev/null +++ b/skins/default/images/buttons/send_act.png diff --git a/skins/default/images/buttons/send_pas.png b/skins/default/images/buttons/send_pas.png Binary files differnew file mode 100644 index 000000000..db227c902 --- /dev/null +++ b/skins/default/images/buttons/send_pas.png diff --git a/skins/default/images/buttons/settings.png b/skins/default/images/buttons/settings.png Binary files differnew file mode 100644 index 000000000..41395bf7c --- /dev/null +++ b/skins/default/images/buttons/settings.png diff --git a/skins/default/images/buttons/source_act.png b/skins/default/images/buttons/source_act.png Binary files differnew file mode 100644 index 000000000..3971b5cee --- /dev/null +++ b/skins/default/images/buttons/source_act.png diff --git a/skins/default/images/buttons/source_pas.png b/skins/default/images/buttons/source_pas.png Binary files differnew file mode 100644 index 000000000..aec440a5e --- /dev/null +++ b/skins/default/images/buttons/source_pas.png diff --git a/skins/default/images/buttons/spacer.gif b/skins/default/images/buttons/spacer.gif Binary files differnew file mode 100644 index 000000000..5bfd67a2d --- /dev/null +++ b/skins/default/images/buttons/spacer.gif diff --git a/skins/default/images/display/confirm.png b/skins/default/images/display/confirm.png Binary files differnew file mode 100644 index 000000000..27265f8a6 --- /dev/null +++ b/skins/default/images/display/confirm.png diff --git a/skins/default/images/display/info.png b/skins/default/images/display/info.png Binary files differnew file mode 100644 index 000000000..85462e4b9 --- /dev/null +++ b/skins/default/images/display/info.png diff --git a/skins/default/images/display/loading.gif b/skins/default/images/display/loading.gif Binary files differnew file mode 100755 index 000000000..4d994852b --- /dev/null +++ b/skins/default/images/display/loading.gif diff --git a/skins/default/images/display/warning.png b/skins/default/images/display/warning.png Binary files differnew file mode 100644 index 000000000..9909617f4 --- /dev/null +++ b/skins/default/images/display/warning.png diff --git a/skins/default/images/icons/abcard.png b/skins/default/images/icons/abcard.png Binary files differnew file mode 100644 index 000000000..d0d850044 --- /dev/null +++ b/skins/default/images/icons/abcard.png diff --git a/skins/default/images/icons/attachment.png b/skins/default/images/icons/attachment.png Binary files differnew file mode 100644 index 000000000..0fcf46499 --- /dev/null +++ b/skins/default/images/icons/attachment.png diff --git a/skins/default/images/icons/dot.png b/skins/default/images/icons/dot.png Binary files differnew file mode 100644 index 000000000..99f736516 --- /dev/null +++ b/skins/default/images/icons/dot.png diff --git a/skins/default/images/icons/flagged.png b/skins/default/images/icons/flagged.png Binary files differnew file mode 100644 index 000000000..58e3e1c2d --- /dev/null +++ b/skins/default/images/icons/flagged.png diff --git a/skins/default/images/icons/folder-closed.png b/skins/default/images/icons/folder-closed.png Binary files differnew file mode 100644 index 000000000..5cbf72a6a --- /dev/null +++ b/skins/default/images/icons/folder-closed.png diff --git a/skins/default/images/icons/folder-inbox.png b/skins/default/images/icons/folder-inbox.png Binary files differnew file mode 100644 index 000000000..995ca8128 --- /dev/null +++ b/skins/default/images/icons/folder-inbox.png diff --git a/skins/default/images/icons/folder-junk.png b/skins/default/images/icons/folder-junk.png Binary files differnew file mode 100644 index 000000000..06fbd49d5 --- /dev/null +++ b/skins/default/images/icons/folder-junk.png diff --git a/skins/default/images/icons/folder-open.png b/skins/default/images/icons/folder-open.png Binary files differnew file mode 100644 index 000000000..09ba4b323 --- /dev/null +++ b/skins/default/images/icons/folder-open.png diff --git a/skins/default/images/icons/folder-sent.png b/skins/default/images/icons/folder-sent.png Binary files differnew file mode 100644 index 000000000..2968ab5e9 --- /dev/null +++ b/skins/default/images/icons/folder-sent.png diff --git a/skins/default/images/icons/folder-trash.png b/skins/default/images/icons/folder-trash.png Binary files differnew file mode 100644 index 000000000..0712aaa71 --- /dev/null +++ b/skins/default/images/icons/folder-trash.png diff --git a/skins/default/images/icons/forwarded.png b/skins/default/images/icons/forwarded.png Binary files differnew file mode 100644 index 000000000..1ea246f8e --- /dev/null +++ b/skins/default/images/icons/forwarded.png diff --git a/skins/default/images/icons/plus.gif b/skins/default/images/icons/plus.gif Binary files differnew file mode 100755 index 000000000..854b5eb34 --- /dev/null +++ b/skins/default/images/icons/plus.gif diff --git a/skins/default/images/icons/replied.png b/skins/default/images/icons/replied.png Binary files differnew file mode 100644 index 000000000..4a5213262 --- /dev/null +++ b/skins/default/images/icons/replied.png diff --git a/skins/default/images/icons/silhouette.png b/skins/default/images/icons/silhouette.png Binary files differnew file mode 100644 index 000000000..c7d97489b --- /dev/null +++ b/skins/default/images/icons/silhouette.png diff --git a/skins/default/images/icons/unread.png b/skins/default/images/icons/unread.png Binary files differnew file mode 100644 index 000000000..31f640632 --- /dev/null +++ b/skins/default/images/icons/unread.png diff --git a/skins/default/images/listheader_aqua.gif b/skins/default/images/listheader_aqua.gif Binary files differnew file mode 100644 index 000000000..59f44ea98 --- /dev/null +++ b/skins/default/images/listheader_aqua.gif diff --git a/skins/default/images/listheader_dark.gif b/skins/default/images/listheader_dark.gif Binary files differnew file mode 100644 index 000000000..cd35555b5 --- /dev/null +++ b/skins/default/images/listheader_dark.gif diff --git a/skins/default/images/listheader_light.gif b/skins/default/images/listheader_light.gif Binary files differnew file mode 100644 index 000000000..8d9e6cac0 --- /dev/null +++ b/skins/default/images/listheader_light.gif diff --git a/skins/default/images/mailbox_list.gif b/skins/default/images/mailbox_list.gif Binary files differnew file mode 100644 index 000000000..d53de17f1 --- /dev/null +++ b/skins/default/images/mailbox_list.gif diff --git a/skins/default/images/mailbox_selected.gif b/skins/default/images/mailbox_selected.gif Binary files differnew file mode 100644 index 000000000..bbc2265e0 --- /dev/null +++ b/skins/default/images/mailbox_selected.gif diff --git a/skins/default/images/rcube_watermark.png b/skins/default/images/rcube_watermark.png Binary files differnew file mode 100644 index 000000000..a9e83e1ad --- /dev/null +++ b/skins/default/images/rcube_watermark.png diff --git a/skins/default/images/roundcube_logo.gif b/skins/default/images/roundcube_logo.gif Binary files differnew file mode 100644 index 000000000..b77fd3d0b --- /dev/null +++ b/skins/default/images/roundcube_logo.gif diff --git a/skins/default/images/roundcube_logo.png b/skins/default/images/roundcube_logo.png Binary files differnew file mode 100644 index 000000000..847d01122 --- /dev/null +++ b/skins/default/images/roundcube_logo.png diff --git a/skins/default/images/roundcube_logo_print.gif b/skins/default/images/roundcube_logo_print.gif Binary files differnew file mode 100644 index 000000000..8fbf6a8eb --- /dev/null +++ b/skins/default/images/roundcube_logo_print.gif diff --git a/skins/default/images/tab_act.gif b/skins/default/images/tab_act.gif Binary files differnew file mode 100644 index 000000000..9329db11f --- /dev/null +++ b/skins/default/images/tab_act.gif diff --git a/skins/default/images/tab_pas.gif b/skins/default/images/tab_pas.gif Binary files differnew file mode 100644 index 000000000..26adabf00 --- /dev/null +++ b/skins/default/images/tab_pas.gif diff --git a/skins/default/includes/header.html b/skins/default/includes/header.html new file mode 100644 index 000000000..b795ad19b --- /dev/null +++ b/skins/default/includes/header.html @@ -0,0 +1,3 @@ +<div id="header"><img src="/images/roundcube_logo.png" width="165" height="55" alt="RoundCube Webmail" /></div> + +<roundcube:object name="message" id="message" />
\ No newline at end of file diff --git a/skins/default/includes/settingscripts.html b/skins/default/includes/settingscripts.html new file mode 100644 index 000000000..b7699708d --- /dev/null +++ b/skins/default/includes/settingscripts.html @@ -0,0 +1,11 @@ +<script type="text/javascript"> + +if (window.rcmail && rcmail.env.action) + var tab = document.getElementById('settingstab'+rcmail.env.action); +else + var tab = document.getElementById('settingstabdefault'); + +if (tab) + tab.className = 'tablink-selected'; + +</script>
\ No newline at end of file diff --git a/skins/default/includes/settingstabs.html b/skins/default/includes/settingstabs.html new file mode 100644 index 000000000..ef561d9f4 --- /dev/null +++ b/skins/default/includes/settingstabs.html @@ -0,0 +1,3 @@ +<div id="tabsbar"> +<span id="settingstabdefault" class="tablink"><roundcube:button command="preferences" type="link" label="preferences" title="editpreferences" /></span><span id="settingstabfolders" class="tablink"><roundcube:button command="folders" type="link" label="folders" title="managefolders" class="tablink" /></span><span id="settingstabidentities" class="tablink"><roundcube:button command="identities" type="link" label="identities" title="manageidentities" class="tablink" /></span> +</div> diff --git a/skins/default/includes/taskbar.html b/skins/default/includes/taskbar.html new file mode 100644 index 000000000..b0ebc552c --- /dev/null +++ b/skins/default/includes/taskbar.html @@ -0,0 +1,14 @@ +<div id="footer"> +<div id="taskbar"> +<roundcube:button command="mail" image="/images/buttons/mail.png" title="mail" width="32" height="32" /> +<roundcube:button command="addressbook" image="/images/buttons/addressbook.png" title="addressbook" width="32" height="32" /> +<roundcube:button command="settings" image="/images/buttons/settings.png" title="settings" width="32" height="32" /> +<roundcube:button command="logout" image="/images/buttons/logout.png" title="logout" width="32" height="32" /> +</div> +</div> + +<!-- +<form name="debugform" style="position:absolute; right:10px; bottom:10px;"> +<textarea name="console" cols="60" rows="15" wrap="off"></textarea> +</form> +-->
\ No newline at end of file diff --git a/skins/default/mail.css b/skins/default/mail.css new file mode 100644 index 000000000..239024c73 --- /dev/null +++ b/skins/default/mail.css @@ -0,0 +1,647 @@ +/***** RoundCube|Mail mail task styles *****/ + + +#messagetoolbar +{ + position: absolute; + top: 20px; + left: 200px; + right: 250px; + height: 35px; +/* border: 1px solid #cccccc; */ +} + +#messagetoolbar a +{ + padding-right: 10px; +} + +#messagetoolbar select +{ + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + color: #333333; +} + +#messagetoolbar select.mboxlist +{ + position: absolute; + left: 300px; + top: 10px; +} + +#messagetoolbar select.mboxlist option +{ + padding-left: 15px; +} + +#messagetoolbar select.mboxlist option[value="0"] +{ + padding-left: 2px; +} + +#listcontrols +{ + position: absolute; + left: 200px; + bottom: 60px; + height: 16px; + width: 400px; +} + +#listcontrols a, +#listcontrols a:active, +#listcontrols a:visited +{ + color: #999999; + font-size: 11px; + text-decoration: none; +} + +#listcontrols a.active, +#listcontrols a.active:active, +#listcontrols a.active:visited +{ + color: #CC0000; +} + +#listcontrols a.active:hover +{ + text-decoration: underline; +} + +#messagecountbar +{ + position: absolute; + top: 35px; + right: 60px; + width: 250px; + height: 20px; + text-align: right; +} + +#messagecountbar span +{ + font-size: 11px; + color: #333333; +} + +#messagepartcontainer +{ + position: absolute; + top: 80px; + left: 20px; + right: 20px; + bottom: 20px; +} + +#mailcontframe +{ + position: absolute; + top: 60px; + left: 200px; + right: 40px; + bottom: 80px; + border: 1px solid #999999; + background-color: #F9F9F9; + overflow: auto; + /* css hack for IE */ + width: expression((parseInt(document.documentElement.clientWidth)-240)+'px'); + height: expression((parseInt(document.documentElement.clientHeight)-140)+'px'); +} + + +#messagepartframe +{ + border: 1px solid #999999; + background-color: #F9F9F9; +} + + +#partheader +{ + position: absolute; + top: 10px; + left: 220px; + height: 40px; +} + +#partheader table td +{ + padding-left: 2px; + padding-right: 4px; + vertical-align: middle; + font-size: 11px; +} + +#partheader table td.title +{ + color: #666666; + font-weight: bold; +} + +#rcmdraglayer +{ + width: 300px; + border: 1px solid #999999; + background-color: #F9F9F9; + padding-left: 8px; + padding-right: 8px; + padding-top: 3px; + padding-bottom: 3px; + font-size: 11px; + opacity: 0.6; + -moz-opacity: 0.6; +} + + +/** mailbox list styles */ + +#mailboxlist-header +{ + position: absolute; + top: 80px; + left: 20px; + width: 140px !important; +/* width: 162px; */ + height: 13px; + padding: 3px 10px 2px 10px; + background-color: #EBEBEB; + background-image: url(images/listheader_aqua.gif); + border: 1px solid #CCCCCC; + color: #333333; + font-size: 11px; + font-weight: bold; +} + +#mailboxlist +{ + position: absolute; + top: 100px; + left: 20px; + width: 160px; + height: auto; + margin: 0px; + padding: 0px; + border: 1px solid #CCCCCC; + background-color: #F9F9F9; + list-style-image: none; + list-style-type: none; +} + +#mailboxlist li +{ + height: 18px; + font-size: 11px; + background: url(images/icons/folder-closed.png) no-repeat; + background-position: 10px 1px; + border-bottom: 1px solid #EBEBEB; +/* IE 5.5 margin-left: -16px; */ +} + +#mailboxlist li.inbox +{ + background-image: url(images/icons/folder-inbox.png); +} + +#mailboxlist li.sent +{ + background-image: url(images/icons/folder-sent.png); +} + +#mailboxlist li.junk +{ + background-image: url(images/icons/folder-junk.png); +} + +#mailboxlist li.trash +{ + background-image: url(images/icons/folder-trash.png); +} + +#mailboxlist li a +{ + display: block; + padding-left: 32px; + padding-top: 2px; + padding-bottom: 2px; + text-decoration: none; +} + +#mailboxlist li, #mailboxlist li.unread +{ + /* background-image: url(images/mailbox_list.gif); */ +} + +#mailboxlist li.unread +{ + font-weight: bold; +} + +#mailboxlist li.selected +{ + background-color: #929292; + border-bottom: 1px solid #898989; +} + +#mailboxlist li.selected a +{ + color: #FFF; + font-weight: bold; +} + + +/** message list styles */ + +body.messagelist +{ + margin: 0px; + background-color: #F9F9F9; +} + +#messagelist +{ + width: 100%; + table-layout: fixed; + /* css hack for IE */ + width: expression(document.getElementById('mailcontframe').clientWidth); +} + +#messagelist thead tr td +{ + height: 20px; + padding-top: 0px; + padding-bottom: 0px; + padding-left: 2px; + padding-right: 4px; + vertical-align: middle; + border-bottom: 1px solid #999999; + color: #333333; + background-color: #EBEBEB; + background-image: url(images/listheader_aqua.gif); + font-size: 11px; + font-weight: bold; +} + +#messagelist tbody tr td +{ + height: 16px !important; + height: 20px; + padding: 2px; + padding-right: 4px; + font-size: 11px; + white-space: nowrap; + border-bottom: 1px solid #EBEBEB; + cursor: pointer; +} + +#messagelist tr td.icon +{ + width: 16px; +} + +#messagelist tr td.subject +{ + overflow: hidden; + text-align: left; +} + +#messagelist tr td.size +{ + width: 60px; + text-align: right; +} + +#messagelist tr td.from, +#messagelist tr td.to +{ + width: 180px; + overflow: hidden; +} + +#messagelist tr td.date +{ + width: 110px; +} + +#messagelist tr.message +{ + background-color: #FFFFFF; +} + +/* +#messagelist tr.odd +{ + background-color: #F9F9F9; +} +*/ + +#messagelist tr.unread +{ + font-weight: bold; + background-color: #FFFFFF; +} + +#messagelist tr.selected td +{ + font-weight: bold; + color: #FFFFFF; + background-color: #CC3333; +} + +#messagelist tr.selected td a.rcmContactAddress +{ + color: #FFFFFF; +} + + +/** message view styles */ + + +#messageframe +{ + position: absolute; + top: 70px; + left: 200px; + right: 40px; + /* css hack for IE */ + margin-bottom: 50px; + width: expression(document.body.clientWidth-240); +} + +table.headers-table +{ + width: 100%; + background-color: #EBEBEB; + table-layout: fixed; + +} + +table.headers-table tr td +{ + font-size: 11px; + border-bottom:1px solid #FFFFFF; +} + +table.headers-table td.header-title +{ + width: 70px; + color: #666666; + font-weight: bold; + text-align: right; + padding-right: 4px; +} + +table.headers-table tr td.subject +{ + width: 95%; + font-weight: bold; +} + +#attachment-list +{ + margin: 0px; + padding: 0px 0px 0px 68px; + height: 18px; + list-style-image: none; + list-style-type: none; + background-color: #DFDFDF; + background: url(images/icons/attachment.png) no-repeat #DFDFDF; + background-position: 52px 1px; + border-bottom: 1px solid #FFFFFF; +} + +#attachment-list li +{ +/* display: block; */ + float: left; + height: 18px; + font-size: 11px; + padding: 2px 10px 0px 10px; +} + +#attachment-list li a +{ + text-decoration: none; +} + +#attachment-list li a:hover +{ + text-decoration: underline; +} + +#messagebody +{ + min-height: 300px; + margin-top: 10px; + margin-bottom: 50px; + background-color: #FFFFFF; + border: 1px solid #cccccc; + border-top: none; +} + +div.message-part +{ + padding: 8px; + padding-top: 10px; + border-top: 1px solid #cccccc; + overflow: hidden; +} + +div.message-part a +{ + color: #0000CC; +} + +div.message-part pre +{ + margin: 0px; + padding: 0px; +} + + +#remote-objects-message +{ + display: none; + height: 20px; + min-height: 20px; + padding: 10px 10px 6px 46px; + margin-top: 8px; +} + +#remote-objects-message a +{ + color: #666666; + padding-left: 10px; +} + +#remote-objects-message a:hover +{ + color: #333333; +} + + +/** message compose styles */ + +#priority-selector +{ + position: absolute; + left: 200px; + top: 10px; +} + +#compose-container +{ + position: absolute; + top: 70px; + left: 200px; + right: 40px; + bottom: 60px; + padding: 0px; + margin: 0px; + /* css hack for IE */ + width: expression(document.documentElement.clientWidth-240); + /* height: expression((parseInt(document.documentElement.clientHeight)-130)+'px'); */ +} + +/* +#compose-headers +{ + position: absolute; + top: 70px; + left: 200px; + height: 84px; + border-top: 1px solid #cccccc; + overflow: auto; +} + +#compose-headers td +{ + padding-top: 1px; + padding-bottom: 1px; + border-right: 1px solid #cccccc; + border-bottom: 1px solid #cccccc; +} +*/ + +#compose-headers +{ + width: 100%; +} + +/* +#compose-headers td +{ + width: 100%; +} +*/ + +#compose-headers td.top +{ + vertical-align: top; +} + +#compose-headers td.title, +#compose-subject td.title +{ + width: 80px !important; + color: #666666; + font-size: 11px; + font-weight: bold; + padding-right: 10px; + white-space: nowrap; +} + +#compose-headers td.add-button +{ + width: 40px !important; + text-align: right; + vertical-align: bottom; +} + +#compose-headers td.add-button a +{ + color: #666666; + font-size: 11px; + text-decoration: none; +} + +#compose-headers td textarea +{ + width: 100%; + height: 40px; +} + +#compose-headers td input +{ + width: 100%; +} + +#compose-cc, +#compose-bcc, +#compose-replyto +{ + display: none; +} + +#compose-body +{ + margin-top: 10px; + width: 100% !important; + width: 95%; + height: 95%; + min-height: 400px; + font-size: 9pt; + font-family: "Courier New", Courier, monospace; +} + +#compose-attachments +{ + position: absolute; + top: 100px; + left: 20px; + width: 160px; +} + +#compose-attachments ul +{ + margin: 0px; + padding: 0px; + border: 1px solid #CCCCCC; + background-color: #F9F9F9; + list-style-image: none; + list-style-type: none; +} + +#compose-attachments ul li +{ + height: 18px; + font-size: 11px; + padding-left: 26px; + padding-top: 2px; + padding-right: 4px; + background: url(images/icons/attachment.png) no-repeat; + background-position: 4px 1px; + border-bottom: 1px solid #EBEBEB; + white-space: nowrap; + overflow: hidden; +} + +#attachment-form +{ + position: absolute; + top: 150px; + left: 20px; + z-index: 200; + padding: 8px; + visibility: hidden; + border: 1px solid #CCCCCC; + background-color: #F9F9F9; +} + +#attachment-form input.button +{ + margin-top: 8px; +} + + diff --git a/skins/default/pngbehavior.htc b/skins/default/pngbehavior.htc new file mode 100644 index 000000000..553699a2f --- /dev/null +++ b/skins/default/pngbehavior.htc @@ -0,0 +1,52 @@ +<public:component> +<public:attach event="onpropertychange" onevent="propertyChanged()" /> +<script> + +var supported = /MSIE (5\.5)|[6789]/.test(navigator.userAgent) && navigator.platform == "Win32"; +var realSrc; +var blankSrc = "skins/default/images/blank.gif"; + +if (supported) fixImage(); + +function propertyChanged() { + if (!supported) return; + + var pName = event.propertyName; + if (pName != "src") return; + // if not set to blank + if ( ! new RegExp(blankSrc).test(src)) + fixImage(); +}; + +function fixImage() { + // get src + var src = element.src; + + // check for real change + if (src == realSrc) { + element.src = blankSrc; + return; + } + + if ( ! new RegExp(blankSrc).test(src)) { + // backup old src + realSrc = src; + element._original_src = realSrc; + } + + // test for png + if ( /\.png$/.test( realSrc.toLowerCase() ) ) { + // set blank image + element.src = blankSrc; + // set filter + element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + + src + "',sizingMethod='scale')"; + } + else { + // remove filter + element.runtimeStyle.filter = ""; + } +} + +</script> +</public:component>
\ No newline at end of file diff --git a/skins/default/print.css b/skins/default/print.css new file mode 100644 index 000000000..ac184db9d --- /dev/null +++ b/skins/default/print.css @@ -0,0 +1,111 @@ +/***** RoundCube|Mail message print styles *****/ + +body +{ + background-color: #ffffff; + color: #000000; + margin: 2mm; +} + +body, td, th, span, div, p, h3 +{ + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + font-size: 9pt; + color: #000000; +} + +h3 +{ + font-size: 18px; + color: #000000; +} + +a, a:active, a:visited +{ + color: #000000; +} + +#header +{ + margin-left: 5mm; + margin-bottom: 3mm; +} + +#messageframe +{ + position: relative; +} + +table.headers-table +{ + table-layout: fixed; +} + +table.headers-table tr td +{ + font-size: 9pt; +} + +table.headers-table td.header-title +{ + color: #666666; + font-weight: bold; + text-align: right; + vertical-align: top; + padding-right: 4mm; + white-space: nowrap; +} + +table.headers-table tr td.subject +{ + width: 90%; + font-weight: bold; +} + +#attachment-list +{ + margin-top: 3mm; + padding-top: 3mm; + border-top: 1pt solid #cccccc; +} + +#attachment-list li +{ + font-size: 9pt; +} + +#attachment-list li a +{ + text-decoration: none; +} + +#attachment-list li a:hover +{ + text-decoration: underline; +} + +#messagebody +{ + margin-top: 5mm; + border-top: none; +} + +div.message-part +{ + padding: 2mm; + margin-top: 5mm; + margin-bottom: 5mm; + border-top: 1pt solid #cccccc; +} + +div.message-part a +{ + color: #0000CC; +} + +div.message-part pre +{ + margin: 0; + padding: 0; + font-size: 9pt; +} diff --git a/skins/default/settings.css b/skins/default/settings.css new file mode 100644 index 000000000..7b4b88beb --- /dev/null +++ b/skins/default/settings.css @@ -0,0 +1,150 @@ +/***** RoundCube|Mail settings task styles *****/ + + +#tabsbar +{ + position: absolute; + top: 42px; + left: 220px; + right: 60px; + height: 22px; + border-bottom: 1px solid #999999; + white-space: nowrap; + /* css hack for IE */ + width: expression((parseInt(document.documentElement.clientWidth)-280)+'px'); +} + +span.tablink, +span.tablink-selected +{ + float: left; + width: 80px; + height: 17px !important; + height: 15px; + padding: 5px 10px 2px 10px; + background: url('images/tab_pas.gif') top left no-repeat; +} + +span.tablink-selected +{ + background: url('images/tab_act.gif') top left no-repeat; +} + +span.tablink a +{ + color: #555555; +} + +span.tablink a, +span.tablink-selected a +{ + text-decoration: none; +} + +#userprefs-box +{ + position: absolute; + top: 90px; + left: 20px; + width: 550px; + border: 1px solid #999999; +} + +#userprefs-box table td.title +{ + color: #666666; + padding-right: 10px; +} + +#identities-list, +#folder-manager +{ + position: absolute; + top: 90px; + left: 20px; +} + +#identities-table +{ + width: 500px; + border: 1px solid #999999; + background-color: #F9F9F9; +} + +#identities-table tbody td +{ + cursor: pointer; +} + +#identity-frame +{ + position: relative; + margin-top: 20px; + border: 1px solid #999999; +} + +#identity-details +{ + margin-top: 30px; + width: 500px; + border: 1px solid #999999; +} + +#identity-details table td.title +{ + color: #666666; + font-weight: bold; + text-align: right; + padding-right: 10px; +} + +#userprefs-title, +#identity-title, +div.boxtitle, +#subscription-table thead td +{ + height: 12px !important; + padding: 4px 20px 3px 6px; + border-bottom: 1px solid #999999; + color: #333333; + font-size: 11px; + font-weight: bold; + background-color: #EBEBEB; + background-image: url(images/listheader_aqua.gif); +} + +div.settingsbox +{ + width: 500px; + margin-top: 20px; + border: 1px solid #999999; +} + +div.settingspart +{ + display: block; + padding: 10px; +} + +#subscription-table +{ + width: 500px; + border: 1px solid #999999; +} + +#subscription-table tbody td +{ + padding-left: 6px; + padding-right: 20px; + white-space: nowrap; + border-bottom: 1px solid #EBEBEB; + background-color: #F9F9F9; +} + +/* +#subscription-table tbody td select +{ + width: 150px; +} +*/ + diff --git a/skins/default/templates/addcontact.html b/skins/default/templates/addcontact.html new file mode 100644 index 000000000..773e6085f --- /dev/null +++ b/skins/default/templates/addcontact.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/addresses.css" /> +</head> +<body class="iframe"> + +<div id="contact-title"><roundcube:label name="addcontact" /></div> + +<div id="contact-details"> +<roundcube:object name="contacteditform" size="40" /> + +<p><br /> +<input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" /> +<roundcube:button command="save" type="input" class="button" label="save" /> +</p> + +</div> + + +</body> +</html> diff --git a/skins/default/templates/addidentity.html b/skins/default/templates/addidentity.html new file mode 100644 index 000000000..0875a99a8 --- /dev/null +++ b/skins/default/templates/addidentity.html @@ -0,0 +1,35 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/settings.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> +<roundcube:include file="/includes/settingstabs.html" /> + + +<div id="identities-list"> +<roundcube:object name="itentitiesList" id="identities-table" class="records-table" cellspacing="0" summary="Identities list" editIcon="" /> + +<p><roundcube:button command="add" type="input" label="newidentity" class="button" /></p> + +<div id="identity-details"> +<div id="identity-title"><roundcube:label name="newidentity" /></div> + +<div style="padding:15px;"> +<roundcube:object name="identityform" size="40" /> + +<p><br /> +<roundcube:button command="save" type="input" class="button" label="save" /> +</p> +</div> +</div> +</div> + +<roundcube:include file="/includes/taskbar.html" /> + +</body> +</html> diff --git a/skins/default/templates/addressbook.html b/skins/default/templates/addressbook.html new file mode 100644 index 000000000..99c2c8efb --- /dev/null +++ b/skins/default/templates/addressbook.html @@ -0,0 +1,37 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/addresses.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> + +<div id="abooktoolbar"> +<roundcube:button command="add" imageAct="/images/buttons/add_contact_act.png" imagePas="/images/buttons/add_contact_pas.png" width="32" height="32" title="newcontact" /> +<roundcube:button command="delete" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletecontact" /> +<roundcube:button command="compose" imageAct="/images/buttons/compose_act.png" imagePas="/images/buttons/compose_pas.png" width="32" height="32" title="composeto" /> +<roundcube:button command="print" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="print" /> +<roundcube:button command="export" imageAct="/images/buttons/download_act.png" imagePas="/images/buttons/download_pas.png" width="32" height="32" title="export" /> +</div> + +<div id="abookcountbar"> +<roundcube:button command="previouspage" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previousmessages" /> + <roundcube:object name="recordsCountDisplay" /> +<roundcube:button command="nextpage" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextmessages" /> +</div> + +<div id="addresslist"> +<roundcube:object name="addresslist" id="contacts-table" class="records-table" cellspacing="0" summary="Contacts list" /> +</div> + +<div id="contacts-box"> +<roundcube:object name="addressframe" id="contact-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" /> +</div> + +<roundcube:include file="/includes/taskbar.html" /> + +</body> +</html> diff --git a/skins/default/templates/compose.html b/skins/default/templates/compose.html new file mode 100644 index 000000000..7ff5f3006 --- /dev/null +++ b/skins/default/templates/compose.html @@ -0,0 +1,118 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>RoundCube|Mail :: <roundcube:label name="compose" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/mail.css" /> +<script type="text/javascript"> +<!-- + +function rcmail_toggle_display(id) + { + var row, disp; + if (row = document.getElementById(id)) + { + disp = (!row.style.display || row.style.display=='none') ? (document.all ? 'block' : 'table-row') : 'none'; + row.style.display = disp; + } + + return false; + } + +//--> +</script> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> + +<form name="form" method="post"> + +<div id="messagetoolbar"> +<roundcube:button command="list" image="/images/buttons/back_act.png" width="32" height="32" title="backtolist" /> +<roundcube:button command="send" imageAct="/images/buttons/send_act.png" imagePas="/images/buttons/send_pas.png" width="32" height="32" title="sendmessage" /> +<roundcube:button command="contacts" imageAct="/images/buttons/contacts_act.png" imagePas="/images/buttons/contacts_pas.png" width="32" height="32" title="addressbook" /> +<roundcube:button command="add-attachment" imageAct="/images/buttons/attach_act.png" imagePas="/images/buttons/attach_pas.png" width="32" height="32" title="addattachment" /> + +<div id="priority-selector"> +<roundcube:label name="priority" />: <roundcube:object name="prioritySelector" form="form" /> +</div> + +</div> + +<div id="compose-container"> +<table border="0" cellspacing="0" cellpadding="1" style="width:100%; height:100%;" summary=""><tr> +<td> + +<table border="0" cellspacing="0" cellpadding="1" id="compose-headers" summary=""><tr> + +<td class="title"><roundcube:label name="from" /></td> +<td><roundcube:object name="composeHeaders" part="from" form="form" /></td> + +</tr><tr> + +<td class="title top"><roundcube:label name="to" /></td> +<td><roundcube:object name="composeHeaders" part="to" form="form" cols="80" rows="2" /></td> +<td class="add-button"><a href="#" onclick="return rcmail_toggle_display('compose-cc')">[Cc]</a><br /> +<a href="#" onclick="return rcmail_toggle_display('compose-bcc')">[Bcc]</a><br /></td> + +</tr><tr id="compose-cc"> + +<td class="title top"><roundcube:label name="cc" /></td> +<td><roundcube:object name="composeHeaders" part="cc" form="form" cols="80" rows="2" /></td> + +</tr><tr id="compose-bcc"> + +<td class="title top"><roundcube:label name="bcc" /></td> +<td><roundcube:object name="composeHeaders" part="bcc" form="form" cols="80" rows="2" /></td> + +</tr><tr id="compose-replyto"> + +<td class="title top"><roundcube:label name="replyto" /></td> +<td><roundcube:object name="composeHeaders" part="replyto" form="form" size="80" /></td> + +<!-- </tr><tr> + +<td><roundcube:object name="composeAttachment" size="20" /></td> --> + +</tr><tr> + +<td class="title"><roundcube:label name="subject" /></td> +<td><roundcube:object name="composeSubject" id="compose-subject" form="form" /></td> + +</tr></table> + +</td> + +</tr><tr> + +<td style="width:100%; height:100%;"> +<roundcube:object name="composeBody" id="compose-body" form="form" cols="80" rows="20" warp="virtual" /> +</td> + +</tr></table> + +</div> + +<div id="compose-attachments"> +<div>Attachments</div> +<roundcube:object name="composeAttachmentList" /> +<p><roundcube:button command="add-attachment" imagePas="/images/buttons/add_pas.png" imageAct="/images/buttons/add_act.png" width="23" height="18" title="addattachment" /> +</div> + +</form> + +<roundcube:object name="composeAttachmentForm" id="attachment-form" /> + +<!-- +<div id="attachment-form"> +<roundcube:object name="composeAttachment" size="20" /><br /> +<input type="button" value="<roundcube:label name='close' />" onclick="rcmail_show_attachment_form(0)" /> +<input type="button" value="<roundcube:label name='send' />" onclick="rcmail.command('send-attachment', this)" /> +</div> +--> + +<roundcube:include file="/includes/taskbar.html" /> + +</body> +</html> diff --git a/skins/default/templates/editcontact.html b/skins/default/templates/editcontact.html new file mode 100644 index 000000000..b71836d5b --- /dev/null +++ b/skins/default/templates/editcontact.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/addresses.css" /> +</head> +<body class="iframe"> + +<div id="contact-title"><roundcube:label name="editcontact" /></div> + +<div id="contact-details"> +<roundcube:object name="contacteditform" size="40" /> + +<p><br /> +<roundcube:button command="show" type="input" class="button" label="cancel" /> +<roundcube:button command="save" type="input" class="button" label="save" /> +</p> + +</div> + + +</body> +</html> diff --git a/skins/default/templates/editidentity.html b/skins/default/templates/editidentity.html new file mode 100644 index 000000000..07283f5be --- /dev/null +++ b/skins/default/templates/editidentity.html @@ -0,0 +1,37 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/settings.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> +<roundcube:include file="/includes/settingstabs.html" /> + + +<div id="identities-list"> +<roundcube:object name="itentitiesList" id="identities-table" class="records-table" cellspacing="0" summary="Identities list" editIcon="" /> + +<p><roundcube:button command="add" type="input" label="newidentity" class="button" /></p> + +<div id="identity-details"> +<div id="identity-title"><roundcube:label name="edititem" /></div> + +<div style="padding:15px;"> +<roundcube:object name="identityform" size="40" /> + +<p><br /> +<roundcube:button command="delete" type="input" class="button" label="delete" /> +<roundcube:button command="save" type="input" class="button" label="save" /> +</p> +</div> +</div> +</div> + +<roundcube:include file="/includes/taskbar.html" /> +<roundcube:include file="/includes/settingscripts.html" /> + +</body> +</html> diff --git a/skins/default/templates/error.html b/skins/default/templates/error.html new file mode 100644 index 000000000..0b2d9b8b9 --- /dev/null +++ b/skins/default/templates/error.html @@ -0,0 +1,16 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>RoundCube|Mail :: ERROR</title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +</head> +<body> + +<div id="header"><img src="/images/roundcube_logo.png" width="165" height="55" alt="RoundCube Webmail" /></div> + +<div style="width:400px; margin:60px auto;"> +$__page_content +</div> + +</body> +</html> diff --git a/skins/default/templates/identities.html b/skins/default/templates/identities.html new file mode 100644 index 000000000..7ae2bf007 --- /dev/null +++ b/skins/default/templates/identities.html @@ -0,0 +1,23 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/settings.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> +<roundcube:include file="/includes/settingstabs.html" /> + +<div id="identities-list"> +<roundcube:object name="itentitiesList" id="identities-table" class="records-table" cellspacing="0" summary="Identities list" editIcon="" /> + +<p><roundcube:button command="add" type="input" label="newidentity" class="button" /></p> +</div> + +<roundcube:include file="/includes/taskbar.html" /> +<roundcube:include file="/includes/settingscripts.html" /> + +</body> +</html> diff --git a/skins/default/templates/login.html b/skins/default/templates/login.html new file mode 100644 index 000000000..7f44f5790 --- /dev/null +++ b/skins/default/templates/login.html @@ -0,0 +1,30 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>Welcome to RoundCube|Mail</title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<style type="text/css"> + +#login-form + { + margin: 50px auto; + width: 350px; + } + +</style> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> + +<div id="login-form"> +<form name="form" action="./" method="post"> +<roundcube:object name="loginform" form="form" /> + +<p style="text-align: center;"><input type="submit" class="button" value="<roundcube:label name="login" />"> + +</form> +</div> + +</body> +</html> diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html new file mode 100644 index 000000000..2c47cb67f --- /dev/null +++ b/skins/default/templates/mail.html @@ -0,0 +1,50 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/mail.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> + +<div id="messagetoolbar"> +<roundcube:button command="compose" imageAct="/images/buttons/compose_act.png" imagePas="/images/buttons/compose_pas.png" width="32" height="32" title="writenewmessage" /> +<roundcube:button command="reply" imageAct="/images/buttons/reply_act.png" imagePas="/images/buttons/reply_pas.png" width="32" height="32" title="replytomessage" /> +<roundcube:button command="forward" imageAct="/images/buttons/forward_act.png" imagePas="/images/buttons/forward_pas.png" width="32" height="32" title="forwardmessage" /> +<roundcube:button command="delete" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletemessage" /> +<roundcube:button command="print" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" /> +</div> + +<div id="messagecountbar"> +<roundcube:button command="previouspage" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previousmessages" /> + <roundcube:object name="messageCountDisplay" /> +<roundcube:button command="nextpage" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextmessages" /> +</div> + +<div id="mailboxlist-header"><roundcube:label name="mailboxlist" /></div> +<roundcube:object name="mailboxlist" id="mailboxlist" /> + +<div id="mailcontframe"> +<roundcube:object name="messages" + id="messagelist" + cellspacing="0" + summary="Message list" + messageIcon="/images/icons/dot.png" + unreadIcon="/images/icons/unread.png" + repliedIcon="/images/icons/replied.png" + attachmentIcon="/images/icons/attachment.png" /> +</div> + +<div id="listcontrols"> +<roundcube:label name="select" />: +<roundcube:button command="select-all" label="all" classAct="active" /> +<roundcube:button command="select-all" prop="unread" label="unread" classAct="active" /> +<roundcube:button command="select-none" label="none" classAct="active" /> +</div> + +<roundcube:include file="/includes/taskbar.html" /> + +</body> +</html> diff --git a/skins/default/templates/managefolders.html b/skins/default/templates/managefolders.html new file mode 100644 index 000000000..99afd5c81 --- /dev/null +++ b/skins/default/templates/managefolders.html @@ -0,0 +1,38 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/settings.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> +<roundcube:include file="/includes/settingstabs.html" /> + +<form name="subscriptionform" action="./"> + +<div id="folder-manager"> +<roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" + cellpadding="1" cellspacing="0" summary="Folder subscription table" + deleteIcon="/images/icons/folder-trash.png" /> + + +<div class="settingsbox"> +<div class="boxtitle"><roundcube:label name="createfolder" /></div> + +<div class="settingspart"> +<roundcube:label name="foldername" />: +<roundcube:object name="createfolder" form="subscriptionform" /> +<roundcube:button command="create-folder" type="input" class="button" label="create" /> +</div> +</div> +</div> + +</form> + +<roundcube:include file="/includes/taskbar.html" /> +<roundcube:include file="/includes/settingscripts.html" /> + +</body> +</html> diff --git a/skins/default/templates/message.html b/skins/default/templates/message.html new file mode 100644 index 000000000..aba6412db --- /dev/null +++ b/skins/default/templates/message.html @@ -0,0 +1,41 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/mail.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> + +<div id="messagetoolbar"> +<roundcube:button command="list" image="/images/buttons/back_act.png" width="32" height="32" title="backtolist" /> +<roundcube:button command="reply" imageAct="/images/buttons/reply_act.png" imagePas="/images/buttons/reply_pas.png" width="32" height="32" title="replytomessage" /> +<roundcube:button command="forward" imageAct="/images/buttons/forward_act.png" imagePas="/images/buttons/forward_pas.png" width="32" height="32" title="forwardmessage" /> +<roundcube:button command="delete" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletemessage" /> +<roundcube:button command="print" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" /> +<roundcube:button command="viewsource" imageAct="/images/buttons/source_act.png" imagePas="/images/buttons/source_pas.png" width="32" height="32" title="viewsource" /> +<roundcube:object name="mailboxlist" type="select" noSelection="moveto" onchange="rcmail.command('moveto', this.options[this.selectedIndex].value)" class="mboxlist" /> +</div> + +<div id="messagecountbar"> +<roundcube:button command="previousmessage" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previousmessages" /> + <roundcube:object name="messageCountDisplay" /> +<roundcube:button command="nextmessage" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextmessages" /> +</div> + +<div id="mailboxlist-header"><roundcube:label name="mailboxlist" /></div> +<roundcube:object name="mailboxlist" id="mailboxlist" /> + +<div id="messageframe"> +<roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" addicon="/images/icons/plus.gif" summary="Message headers" /> +<roundcube:object name="messageAttachments" id="attachment-list" /> +<roundcube:object name="blockedObjects" id="remote-objects-message" /> +<roundcube:object name="messageBody" id="messagebody" showImages="true" /> +</div> + +<roundcube:include file="/includes/taskbar.html" /> + +</body> +</html> diff --git a/skins/default/templates/messagepart.html b/skins/default/templates/messagepart.html new file mode 100644 index 000000000..924342f3f --- /dev/null +++ b/skins/default/templates/messagepart.html @@ -0,0 +1,22 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/mail.css" /> +</head> +<body class="extwin"> + +<roundcube:include file="/includes/header.html" /> + +<div id="partheader"> +<roundcube:object name="messagePartControls" cellpadding="2" cellspacing="0" /> +</div> + + +<div id="messagepartcontainer"> +<roundcube:object name="messagePartFrame" id="messagepartframe" width="100%" height="85%" /> +</div> + +</body> +</html> diff --git a/skins/default/templates/printmessage.html b/skins/default/templates/printmessage.html new file mode 100644 index 000000000..223c98b4d --- /dev/null +++ b/skins/default/templates/printmessage.html @@ -0,0 +1,18 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/print.css" /> +</head> +<body> + +<div id="header"><img src="/images/roundcube_logo_print.gif" width="182" height="50" alt="RoundCube Webmail" /></div> + +<div id="messageframe"> +<roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" /> +<roundcube:object name="messageAttachments" id="attachment-list" /> +<roundcube:object name="messageBody" id="messagebody" showImages="false" /> +</div> + +</body> +</html> diff --git a/skins/default/templates/settings.html b/skins/default/templates/settings.html new file mode 100644 index 000000000..b29734d5e --- /dev/null +++ b/skins/default/templates/settings.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/settings.css" /> +</head> +<body> + +<roundcube:include file="/includes/header.html" /> +<roundcube:include file="/includes/settingstabs.html" /> + +<div id="userprefs-box"> +<div id="userprefs-title"><roundcube:label name="userpreferences" /></div> + +<div style="padding:15px"> +<roundcube:object name="userprefs"> + +<p><br /><roundcube:button command="save" type="input" class="button" label="save" /></p> +</div> +</div> + + +<roundcube:include file="/includes/taskbar.html" /> +<roundcube:include file="/includes/settingscripts.html" /> + +</body> +</html> diff --git a/skins/default/templates/showcontact.html b/skins/default/templates/showcontact.html new file mode 100644 index 000000000..1e24688dc --- /dev/null +++ b/skins/default/templates/showcontact.html @@ -0,0 +1,19 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title><roundcube:object name="pagetitle" /></title> +<link rel="stylesheet" type="text/css" href="/common.css" /> +<link rel="stylesheet" type="text/css" href="/addresses.css" /> +</head> +<body class="iframe"> + +<div id="contact-title"><roundcube:object name="contactdetails" part="name" /></div> + +<div id="contact-details"> +<roundcube:object name="contactdetails" /> + +<p><br /><roundcube:button command="edit" type="input" class="button" label="editcontact" /></p> +</div> + +</body> +</html> diff --git a/skins/default/watermark.html b/skins/default/watermark.html new file mode 100644 index 000000000..85e53652a --- /dev/null +++ b/skins/default/watermark.html @@ -0,0 +1,12 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +</head> +<body style="background-color:#F2F2F2;"> + +<div style="margin:10px auto; text-align:center"> +<img src="images/rcube_watermark.png" width="245" height="245" alt="" /> +</div> + +</body> +</html>
\ No newline at end of file diff --git a/temp/.htaccess b/temp/.htaccess new file mode 100644 index 000000000..8e6a345dc --- /dev/null +++ b/temp/.htaccess @@ -0,0 +1,2 @@ +Order allow,deny +Deny from all
\ No newline at end of file |