From: Jérémy Bobbio Date: Sat, 18 Jun 2011 15:02:35 +0000 (+0200) Subject: Imported Upstream version 0.2~stable X-Git-Url: https://git.donarmstrong.com/?p=roundcube.git;a=commitdiff_plain;h=3adad46e27086084a8b28a32fc4fbc953dbfef6c Imported Upstream version 0.2~stable --- diff --git a/._INSTALL b/._INSTALL deleted file mode 100644 index 9293550..0000000 Binary files a/._INSTALL and /dev/null differ diff --git a/._README b/._README deleted file mode 100644 index 7cc650c..0000000 Binary files a/._README and /dev/null differ diff --git a/._UPGRADING b/._UPGRADING deleted file mode 100644 index 64a3886..0000000 Binary files a/._UPGRADING and /dev/null differ diff --git a/.htaccess b/.htaccess index ae20a9a..fcb8f6c 100644 --- a/.htaccess +++ b/.htaccess @@ -1,28 +1,26 @@ # AddDefaultCharset UTF-8 AddType text/x-component .htc - - php_flag display_errors Off - php_flag log_errors On - php_value error_log logs/errors - php_value upload_max_filesize 5M - php_value post_max_size 6M - php_value memory_limit 64M - php_value session.auto_start 0 - php_value zlib.output_compression 0 - php_value magic_quotes_gpc 0 - - - php_flag display_errors Off - php_flag log_errors On - php_value error_log logs/errors - php_value upload_max_filesize 5M - php_value post_max_size 6M - php_value memory_limit 64M - php_value session.auto_start 0 - php_value zlib.output_compression 0 - php_value magic_quotes_gpc 0 +php_flag display_errors Off +php_flag log_errors On +php_value error_log logs/errors + +php_value upload_max_filesize 5M +php_value post_max_size 6M +php_value memory_limit 64M + +php_value zlib.output_compression 0 +php_value magic_quotes_gpc 0 +php_value zend.ze1_compatibility_mode 0 + +php_value session.auto_start 0 +php_value session.gc_maxlifetime 21600 +php_value session.gc_divisor 500 +php_value session.gc_probability 1 + +# http://bugs.php.net/bug.php?id=30766 +php_value mbstring.func_overload 0 @@ -30,5 +28,10 @@ AddType text/x-component .htc Deny from all + +RewriteEngine On +RewriteRule ^favicon.ico$ skins/default/images/favicon.ico + + Order deny,allow Allow from all diff --git a/CHANGELOG b/CHANGELOG index d0fa0e4..6a2b489 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,159 @@ CHANGELOG RoundCube Webmail --------------------------- +- Fix mark popup in IE 7 (#1485369) +- Fix line-break issue when copy & paste in Firefox (#1485425) +- Fix autocomplete "unknown server error" (#1485637) +- Fix STARTTLS before AUTH in SMTP connection (#1484883) +- Support multiple quota values in QUOTAROOT resonse (#1485626) +- Only abbreviate file name for IE < 7 browsers (#1485063) +- Performance: allow setting imap rootdir and delimiter before connect (#1485172) +- Fix sorting of folders with more than 2 levels (#1485569) +- Fix search results page jumps in LDAP addressbook (#1485253) +- Fix empty line before the signature in IE (#1485351) +- Fix horizontal scrollbar in preview pane on IE (#1484633) +- Add Robots meta tag in login page and installer (#1484846) +- Added 'show_images' option, removed 'addrbook_show_images' (#1485597) +- Option to check for new mails in all folders (#1484374) +- Don't set client busy when checking for new messages (#1485276) +- Allow UTF-8 folder names in config (#1485579) +- Add junk_mbox option configuration in installer (#1485579) +- Do serverside addressbook queries for autocompletion (#1485531) +- Allow setting attachment col position in 'list_cols' option +- Allow override 'list_cols' via skin (#1485577) +- Fix 'cache' table cleanup on session destroy (#1485516) +- Increase speed of session destroy and garbage clean up +- Fix session timeout when DB server got clock skew (#1485490) +- Fix handling of some malformed messages (#1484438) +- Speed up raw message body handling +- Better HTML entities conversion in html2text (#1485519) +- Fix big memory consumption and speed up searching on servers without SORT capability +- Fix setting locale to tr_TR, ku and az_AZ (#1485470) +- Use SORT for searching on servers with SORT capability +- Added message status filter +- Fix empty file sending (#1485389) +- Improved searching with many criterias (calling one SEARCH command) +- Fix HTML editor initialization on IE (#1485304) +- Add warning when switching editor mode from html to plain (#1485488) +- Make identities list scrollable (#1485538) +- Fix problem with numeric folder names (#1485527) +- Added BYE response simple support to prevent from endless loops in imap.inc (#1483956) +- Fix unread message unintentionally marked as read if read_when_deleted=true (#1485409) +- Remove port number from SERVER_NAME in smtp_helo_host (#1485518) +- Don't send disposition notification receipts for messages marked as 'read' (#1485523) +- Added 'keep_alive' and 'min_keep_alive' options (#1485360) +- Added option 'identities_level', removed 'multiple_identities' +- Allow deleting identities when multiple_identities=false (#1485435) +- Added option focus_on_new_message (#1485374) +- Fix html2text class autoloading on Windows (#1485505) +- Fix html signature formatting when identity save error occured (#1485426) +- Add feedback and set busy when moving folder (#1485497) +- Fix 'Empty' link visibility for some languages e.g. Slovak (#1485489) +- Fix messages count bar overlapping (#1485270) +- Fix adding signature in drafts compose mode (#1485484) +- Fix iil_C_Sort() to support very long and/or divided responses (#1485283) +- Fix matching case sensitivity when setting identity on reply (#1485480) +- Prefer default identity on reply +- Fix imap searching on ISMail server (#1485466) +- Add css class for flagged messages (#1485464) +- Write username instead of id in sendmail log (#1485477) +- Fix htmlspecialchars() use for PHP version < 5.2.3 (#1485475) +- Fix js keywords escaping in json_serialize() for IE/Opera (#1485472) +- Added bin/killcache.php script (#1485434) +- Add support for SJIS, GB2312, BIG5 in rc_detect_encoding() +- Fix vCard file encoding detection for non-UTF-8 strings (#1485410) +- Add 'skip_deleted' option in User Preferences (#1485445) +- Minimize "inline" javascript scripts use (#1485433) +- Fix css class setting for folders with names matching defined classes names (#1485355) +- Fix race conditions when changing mailbox +- Fix spellchecking when switching to html editor (#1485362) +- Fix compose window width/height (#1485396) +- Allow calling msgimport.sh/msgexport.sh from any directory (#1485431) +- Localized filesize units (#1485340) +- Better handling of "no identity" and "no email in identity" situations (#1485117) +- Added 'mime_param_folding' option with possibility to choose long/non-ascii attachment names encoding eg. to be readable in MS Outlook/OE (#1485320) +- Added "advanced options" feature in User Preferences +- Fix unread counter when displaying cached massage in preview panel (#1485290) +- Fix htmleditor spellchecking on MS Windows (#1485397) +- Fix problem with non-ascii attachment names in Mail_mime (#1485267, #1485096) +- Fix language autodetection (#1485401) +- Fix button label in folders management (#1485405) +- Fix collapsed folder not indicating unread msgs count of all subfolders (#1485403) +- Fix handling of apostrophes in filenames decoded according to rfc2231 + +RELEASE 0.2-BETA + +- Made config files location configurable (#1485215) +- Reduced memory footprint when forwarding attachments (#1485345) +- Allow and use spellcheck attribute for input/textarea fields (#1485060) +- Added icons for forwarded/forwarded+replied messages (#1485257) +- Added Reply-To to forwarded emails (#1485315) +- Display progress message for folders create/delete/rename (#1485357) +- Smart Tags and NOBR tag support in html messages (#1485363, #1485327) +- Redesign of the identities settings (#1484042) +- Add config option to disable creation/deletion of identities (#1484498) +- Added 'sendmail_delay' option to restrict messages sending interval (#1484491) +- Added vertical splitter for folders list resizing +- Added possibility to view all headers in message view +- Fixed splitter drag/resize on Opera (#1485170) +- Fixed quota img height/width setting from template (#1484857) +- Refactor drag & drop functionality. Don't rely on browser events anymore (#1484453) +- Insert "virtual" folders in subscription list (#1484779) +- Added link to open message in new window +- Enable export of address book contacts as vCard +- Add feature to import contacts from vcard files (#1326103) +- Respect Content-Location headers in multipart/related messages according to RFC2110 (#1484946) +- Allowed max. attachment size now indicated in compose screen (#1485030) +- Also capture backspace key in list mode (#1484566) +- Allow application/pgp parts to be displayed (#1484753) +- Correctly handle options in mailto-links (#1485228) +- Immediately save sort_col/sort_order in user prefs (#1485265) +- Truncate very long (above 50 characters) attachment filenames when displaying +- Allow to auto-detect client language if none set (#1484434) +- Auto-detect the client timezone (user configurable) +- Add RFC2231 header value continuations support for attachment filenames + hack for servers that not support that feature +- Fix Reply-To header displaying (#1485314) +- Mark form buttons that provide the most obvious operation (mainaction) +- Added option 'quota_zero_as_unlimited' (#1484604) +- Added PRE handling in html2text class (#1484740) +- Added folder hierarchy collapsing +- Added options to use syslog instead of log file (#1484850) +- Added Logging & Debugging section in Installer +- Fix In-Reply-To and References headers when composing saved draft message (#1485288) +- Fix html message charset conversion for charsets with underline (#1485287) +- Fix buttons status after contacts deletion (#1485233) +- Fix escaping of To: and From: fields when building message body for reply or forward in the HTML editor (#1484904) +- Use current mailbox name in template (#1485256) +- Better fix for skipping untagged responses (#1485261) +- Added pspell support patch by Kris Steinhoff (#1483960) +- Enable spellchecker for HTML editor (#1485114) +- Respect spellcheck_uri in tinyMCE spellchecker (#1484196) +- Case insensitive contacts searching using PostgreSQL (#1485259) +- Make default imap folders configurable for each user (#1485075) +- Save outgoing mail to selectable folder (#1324581) +- Fix hiding of mark menu when clicking th button again (#1484944) +- Use long date format in print mode (#1485191) +- Updated TinyMCE to version 3.1.0.1 +- Re-enable autocomplete attribute for login form (#1485211) +- Check PERMANENTFLAGS before saving $MDNSent flag (#1484963, #1485163) +- Added flag column on messages list (#1484623) +- Patched Mail/MimePart.php (http://pear.php.net/bugs/bug.php?id=14232) +- Allow trash/junk subfolders to be purged (#1485085) +- Store compose parameters in session and redirect to a unique URL +- Fixed CRAM-MD5 authentication (#1484819) +- Fixed forwarding messages with one HTML attachment (#1484442) +- Fixed encoding of message/rfc822 attachments and image/pjpeg handling (#1484914) +- Added option to select skin in user preferences +- Added option to configure displaying of attached images below the message body +- Added option to display images in messages from known senders (#1484601) +- User preferences grouped in more fieldsets +- Fix corrupted MIME headers of messages in Sent folder (#1485111) +- Fixed bug in MDB2 package: http://pear.php.net/bugs/bug.php?id=14124 +- Use keypress instead of keydown to select list's row (#1484816) +- Don't call expunge and don't remove message row after message move if flag_for_deletion is set to true (#1485002) + +RELEASE 0.2-ALPHA + - Added option to disable autocompletion from selected LDAP address books (#1484922) - TLS support in LDAP connections: 'use_tls' property (#1485104) - Fixed removing messages from search set after deleting them (#1485106) @@ -49,214 +202,3 @@ CHANGELOG RoundCube Webmail - Removed lines wrapping when displaying message - Fixed month localization - Changed codebase to PHP5 with autoloader - -RELEASE 0.1.1 - -- Clear selection when selecting single item (#1484942) -- Remove hard-coded image size in skin templates (#1484893) -- Database schema improvements (dropped unnecessary indexes) -- Fixed creating a new folder with a comma in its name (#1484681) -- Fixed sorting of messages when default mailbox is empty (#1484317) -- Improve message previewpane - less loading (#1484316) -- Fixed login form autoompletion (#1484839) -- Fixed virtuser_query option for mdb2 backend (#1484874) -- Fixed attachment resoting from Drafts when message body was empty (#1484506) -- Fixed usage of ob_gzhandler (#1484851) -- Fixed message part window in IE6 (#1484610) -- Fixed decoding of mime-encoded strings (#1484191) -- Fixed some iconv/mb_string problems (#1484598) -- Correctly quote mailbox name when using in URL (#1484313) -- Fixed "headers already sent" errors (#1484860) - -RELEASE 0.1-STABLE - -- Added interactive installer script -- Fix folder adding/renaming inspired by #1484800 -- Localize folder name in page title (#1484785) -- Fix code using wrong variable name (#1484018) -- Allow to send mail with BCC recipients only -- condense TinyMCE toolbar down to one line, removing table buttons (#1484747) -- Add function to mark the selected messages as read/unread (#1457360) -- Also do charset decoding as suggested in RFC 2231 (fix #1484321) -- Show message count in folder list and hint when creating a subfolder -- Distinguish ssl and tls for imap connections (#1484667) -- Added some charset aliases to fix typical mis-labelling (#1484565) -- Remember decision to display images for a certain message during session (#1484754) -- Truncate attachment filenames to 55 characters due to an IE bug (#1484757) -- Make sending of read receipts configurable -- Respect config when localize folder names (#1484707) -- Also respect receipt and priority settings when re-opening a draft message -- Remember search results (closes #1483883), patch by the_glu -- Add Received header on outgoing mail -- Upgrade to TinyMCE 2.1.3 -- Allow inserting image attachments into HTML messages while composing (#1484557) -- Implement Message-Disposition-Notification (Receipts) -- Fix overriding of session vars when register_globals is on (#1484670) -- Fix bug with case-sensitive folder names (#1484245) -- Don't create default folders by default -- Fixed some potential security risks (audited by Andris) -- Only show new messages if they match the current search (#1484176) -- Switch to/from when searcing in Sent folder (#1484555) -- Correctly read the References header (#1484646) -- Unset old cookie before sending a new value (#1484639) -- Correctly decode attachments when downloading them (#1484645 and #1484642) -- Suppress IE errors when clearing attachments form (#1484356) -- Log error when login fails due to auto_create_user turned off -- Filter linked/imported CSS files (closes #1484056) -- Improve message compose screen (closes #1484383) -- Select next row after removing one from list (#1484387) - -RELEASE 0.1-RC2 - -- Enable drag-&-dropping of folders to a new parent and allow to create subfolders (#1457344) -- Suppress IE errors when clearing attachments form (#1484356) -- Set preferences field in user table to NULL (#1484386) -- Log error when login fails due to auto_create_user turned off -- Filter linked/imported CSS files (closes #1484056) -- Improve message compose screen (closes #1484383) -- Select next row after removing one from list (#1484387) -- Make smtp HELO/EHLO hostname configurable (#1484067) -- IPv6 Compatability (#1484322), Patch #1484373 -- Unlock interface when message sending fails (#1484570) -- Eval PHP code in template includes (if configured) -- Show message when folder is empty. Mo more static text in table (#1484395) -- Only display unread count in page title when new messages arrived -- Fixed wrong delete button tooltip (#1483965) -- Fixed charset encoding bug (#1484429) -- Applied patch for LDAP version (#1484552) -- Improved XHTML validation -- Fix message list selection (#1484550) -- Better fix lowercased usernames (#1484473) -- Update pngbehavior Script as suggested in #1484490 -- Fixed moving/deleting messages when more than 1 is selected -- Applied patch for LDAP contacts listing by Glen Ogilvie -- Applied patch for more address fields in LDAP contacts (#1484402) -- Add alternative for getallheaders() (fix #1484508) -- Identify mailboxes case-sensitive -- Sort mailbox list case-insensitive (closes #1484338) -- Fix display of multipart messages from Apple Mail (closes #1484027) -- Protect AJAX request from being fetched by a foreign site (XSS) -- Make autocomplete for loginform configurable by the skin template -- Fix compose function from address book (closes #1484426) -- Added //IGNORE to iconv call (patch #1484420, closes #1484023) -- Check if mbstring supports charset (#1484290 and #1484292) -- Prefer iconv over mbstring (as suggested in #1484292) -- Check filesize of template includes (#1484409) -- Fixed bug with buttons not dimming/enabling properly after switching folders -- Fixed compose window becoming unresponsive after saving a draft (#1484487) -- Re-enabled "Back" button in compose window now that bug #1484487 is fixed -- Fixed unresponsive interface issue when downloading attachments (#1484496) -- Lowered status message time from 5 to 3 seconds to improve responsiveness -- Raised .htaccess upload_max_filesize from 2M to 5M to differ from default php.ini -- Increased "mailboxcontrols" mail.css width from 160 to 170px to fix non-english languages (#1484499) -- Fix status message bug #1484464 with regard to #1484353 -- Fix address adding bug reported by David Koblas -- Applied socket error patch by Thomas Mangin -- Pass-by-reference workarround for PHP5 in sendmail.inc -- Fixed buggy imap_root settings (closes #1484379) -- Prevent default events on subject links (#1484399) -- Use HTTP-POST requests for actions that change state - -RELEASE 0.1-RC1 - -- Use global filters and bind username/ for Ldap searches (#1484159) -- Hide quota display if imap server does not support it -- Hide address groups if no LDAP servers configured -- Add link to message subjects (closes #1484257) -- Better SQL query for contact listing/search (closes #1484369) -- Fixed marking as read in preview pane (closes #1484364) -- CSS hack to display attachments correctly in IE6 -- Wrap message body text (closes #1484148) -- LDAP access is back in address book (closes #1484087) -- Added search function for contacts -- New Template parsing and output encoding -- Fixed bugs #1484119 and #1483978 -- Fixed message moving procedure (closes #1484308) -- Fixed display of multiple attachments (closes #1466563) -- Fixed check for new messages (closes #1484310) -- List attachments without filename -- New session authentication: Change sessid cookie when login, authentication with sessauth cookie is now configurable. - Should close bugs #1483951 and #1484299 -- Correctly translate mailbox names (closes #1484276) -- Quote e-mail address links (closes #1484300) -- Updated PEAR::Mail_mime package -- Accept single quotes for HTML attributes when modifying message body (thanks Jason) -- Sanitize input for new users/identities (thanks Colin Alston) -- Don't download HTML message parts -- Convert HTML parts to plaintext if 'prefer_html' is off -- Correctly parse message/rfc822 parts (closes #1484045) -- Also use user_id for unique key in messages table (closes #1484074) -- Hide contacts drop down on blur (closes #1484203) -- Make entries in contacts drop down clickable -- Turn off browser autocompletion on login page -- Quote GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost IDENTIFIED BY 'password'; > quit -# mysql roundcubemail < SQL/mysql.initial.sql - - -* MySQL 4.1.x/5.x ------------------ -For MySQL version 4.1 and up, it's recommended to create the database for -RoundCube with utf-8 charset. Here's an example of the init procedure: - -# mysql -> CREATE DATABASE roundcubemail DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; -> GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost - IDENTIFIED BY 'password'; -> quit -# mysql roundcubemail < SQL/mysql5.initial.sql +# mysql roundcubemail < SQL/mysql.initial.sql -Note: 'password' is the master password for the roundcube user. It is strongly +Note 1: 'password' is the master password for the roundcube user. It is strongly recommended you replace this with a more secure password. Please keep in mind: You need to specify this password later in 'config/db.inc.php'. +Note 2: For MySQL version 4.1 and up, it's recommended to create the database for +RoundCube with utf-8 charset. + * SQLite -------- diff --git a/LICENSE b/LICENSE index 7ade7a6..d511905 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,8 @@ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -16,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -56,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -111,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -169,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -226,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -279,3 +278,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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. + + This program 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README b/README index e23c494..d7a13f7 100644 --- a/README +++ b/README @@ -1,17 +1,17 @@ RoundCube Webmail (http://roundcube.net) -================= + Introduction: ------------- RoundCube Webmail is a browser-based multilingual IMAP client with an application-like user interface. It provides full functionality you expect -from an e-mail client, including MIME support, address book, folder manipulation, +from an e-mail client, including MIME support, address book, folder management, message searching and spell checking. RoundCube Webmail is written in PHP and -requires the MySQL or Postgres database. The user interface is fully skinnable -using XHTML and CSS 2. +requires the MySQL, PostgreSQL or SQLite database. The user interface is fully +skinnable using XHTML and CSS 2. This project is meant to be a modern webmail solution which is easy to -install/configure and that runs on a standard PHP plus MySQL or Postgres +install/configure and that runs on a standard PHP plus MySQL, PostgreSQL or SQLite configuration. It includes open-source classes/libraries like PEAR (http://pear.php.net) and the IMAP wrapper from IlohaMail (http://www.ilohamail.org). @@ -24,27 +24,21 @@ Installation: ------------- For detailed instructions on how to install RoundCube webmail on your server, please refer to the INSTALL document in the same directory as this document. -Please carefully read the REQUIREMENTS section of the INSTALL instructions. +Please carefully read the REQUIREMENTS section of the INSTALL instructions. -Licensing: +Licensing: ---------- This product is distributed under the GPL. Please read through the file LICENSE for more information about our license. -How it works: +Contribution: ------------- -The main authority for the RoundCube access is the IMAP server. If -'auto_create_user' is set to TRUE in config/main.inc.php a new record in the -user table will be created once the IMAP login succeeded. This user record does -not store a password, it's just used to assign identities, contacts and cache -records. If you have 'auto_create_user' set to FALSE only IMAP logins which -already have a corresponding entry in the user's table (username and hostname) -will be allowed. - -Read more at http://trac.roundcube.net/wiki/Howto_Config -and http://trac.roundcube.net/wiki/Dev_Docs +Want to help make RoundCube the best webmail solution ever? +RoundCube is open source software. Our developers and contributors all +are volunteers and we're always looking for new additions and resources. +For more information visit http://roundcube.net/contribute Contact: @@ -56,5 +50,3 @@ for details. You're always welcome to send a message to the project admin: hello@roundcube.net - - diff --git a/SQL/mssql.initial.sql b/SQL/mssql.initial.sql index f3b5d5c..534e327 100644 --- a/SQL/mssql.initial.sql +++ b/SQL/mssql.initial.sql @@ -1,7 +1,6 @@ CREATE TABLE [dbo].[cache] ( [cache_id] [int] IDENTITY (1, 1) NOT NULL , [user_id] [int] NOT NULL , - [session_id] [varchar] (32) COLLATE Latin1_General_CI_AI NULL , [cache_key] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , [created] [datetime] NOT NULL , [data] [text] COLLATE Latin1_General_CI_AI NOT NULL @@ -31,7 +30,8 @@ CREATE TABLE [dbo].[identities] ( [email] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , [reply-to] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , [bcc] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , - [signature] [text] COLLATE Latin1_General_CI_AI NOT NULL + [signature] [text] COLLATE Latin1_General_CI_AI NOT NULL, + [html_signature] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO @@ -70,7 +70,7 @@ CREATE TABLE [dbo].[users] ( [alias] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , [created] [datetime] NOT NULL , [last_login] [datetime] NULL , - [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL , + [language] [varchar] (5) COLLATE Latin1_General_CI_AI NULL , [preferences] [text] COLLATE Latin1_General_CI_AI NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO @@ -119,7 +119,6 @@ GO ALTER TABLE [dbo].[cache] ADD CONSTRAINT [DF_cache_user_id] DEFAULT ('0') FOR [user_id], - CONSTRAINT [DF_cache_session_id] DEFAULT (null) FOR [session_id], CONSTRAINT [DF_cache_cache_key] DEFAULT ('') FOR [cache_key], CONSTRAINT [DF_cache_created] DEFAULT (getdate()) FOR [created] GO @@ -130,7 +129,7 @@ GO CREATE INDEX [IX_cache_cache_key] ON [dbo].[cache]([cache_key]) ON [PRIMARY] GO - CREATE INDEX [IX_cache_session_id] ON [dbo].[cache]([session_id]) ON [PRIMARY] + CREATE INDEX [IX_cache_created] ON [dbo].[cache]([created]) ON [PRIMARY] GO ALTER TABLE [dbo].[contacts] ADD @@ -184,10 +183,10 @@ GO CREATE INDEX [IX_messages_cache_key] ON [dbo].[messages]([cache_key]) ON [PRIMARY] GO - CREATE INDEX [IX_messages_idx] ON [dbo].[messages]([idx]) ON [PRIMARY] + CREATE INDEX [IX_messages_uid] ON [dbo].[messages]([uid]) ON [PRIMARY] GO - CREATE INDEX [IX_messages_uid] ON [dbo].[messages]([uid]) ON [PRIMARY] + CREATE INDEX [IX_messages_created] ON [dbo].[messages]([created]) ON [PRIMARY] GO ALTER TABLE [dbo].[session] ADD @@ -196,11 +195,19 @@ ALTER TABLE [dbo].[session] ADD CONSTRAINT [DF_session_ip] DEFAULT ('') FOR [ip] GO + CREATE INDEX [IX_session_changed] ON [dbo].[session]([changed]) ON [PRIMARY] +GO + ALTER TABLE [dbo].[users] ADD CONSTRAINT [DF_users_username] DEFAULT ('') FOR [username], CONSTRAINT [DF_users_mail_host] DEFAULT ('') FOR [mail_host], CONSTRAINT [DF_users_alias] DEFAULT ('') FOR [alias], CONSTRAINT [DF_users_created] DEFAULT (getdate()) FOR [created], - CONSTRAINT [DF_users_language] DEFAULT ('en') FOR [language] +GO + + CREATE INDEX [IX_users_username] ON [dbo].[users]([username]) ON [PRIMARY] +GO + + CREATE INDEX [IX_users_alias] ON [dbo].[users]([alias]) ON [PRIMARY] GO diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql index fadc072..5a1bfd1 100644 --- a/SQL/mysql.initial.sql +++ b/SQL/mysql.initial.sql @@ -1,125 +1,131 @@ -- RoundCube Webmail initial database structure --- Version 0.1 --- +-- Version 0.2 -- -------------------------------------------------------- --- --- Table structure for table `cache` --- +/*!40014 SET FOREIGN_KEY_CHECKS=0 */; -CREATE TABLE `cache` ( - `cache_id` int(10) unsigned NOT NULL auto_increment, - `user_id` int(10) unsigned NOT NULL default '0', - `session_id` varchar(40) 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`), - INDEX `user_cache_index` (`user_id`,`cache_key`) -); --- -------------------------------------------------------- +-- Table structure for table `session` --- --- Table structure for table `contacts` --- +CREATE TABLE `session` ( + `sess_id` varchar(40) NOT NULL, + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `changed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `ip` varchar(40) NOT NULL, + `vars` text NOT NULL, + PRIMARY KEY(`sess_id`), + INDEX `changed_index` (`changed`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -CREATE TABLE `contacts` ( - `contact_id` int(10) unsigned NOT NULL auto_increment, - `user_id` int(10) unsigned NOT NULL default '0', - `changed` datetime NOT NULL default '0000-00-00 00:00:00', - `del` tinyint(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`) -); --- -------------------------------------------------------- +-- Table structure for table `users` --- --- Table structure for table `identities` --- +CREATE TABLE `users` ( + `user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `username` varchar(128) NOT NULL, + `mail_host` varchar(128) NOT NULL, + `alias` varchar(128) NOT NULL, + `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), + `preferences` text, + PRIMARY KEY(`user_id`), + INDEX `username_index` (`username`), + INDEX `alias_index` (`alias`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -CREATE TABLE `identities` ( - `identity_id` int(10) unsigned NOT NULL auto_increment, - `user_id` int(10) unsigned NOT NULL default '0', - `del` tinyint(1) NOT NULL default '0', - `standard` tinyint(1) NOT NULL default '0', - `name` varchar(128) NOT NULL, - `organization` varchar(128) NOT NULL default '', - `email` varchar(128) NOT NULL, - `reply-to` varchar(128) NOT NULL default '', - `bcc` varchar(128) NOT NULL default '', - `signature` text NOT NULL default '', - `html_signature` tinyint(1) NOT NULL default '0', - PRIMARY KEY (`identity_id`), - KEY `user_id` (`user_id`) -); --- -------------------------------------------------------- +-- Table structure for table `messages` --- --- Table structure for table `session` --- +CREATE TABLE `messages` ( + `message_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `del` tinyint(1) NOT NULL DEFAULT '0', + `cache_key` varchar(128) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL, + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `idx` int(11) UNSIGNED NOT NULL DEFAULT '0', + `uid` int(11) UNSIGNED NOT NULL DEFAULT '0', + `subject` varchar(255) NOT NULL, + `from` varchar(255) NOT NULL, + `to` varchar(255) NOT NULL, + `cc` varchar(255) NOT NULL, + `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `size` int(11) UNSIGNED NOT NULL DEFAULT '0', + `headers` text NOT NULL, + `structure` text, + PRIMARY KEY(`message_id`), + INDEX `created_index` (`created`), + UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`), + CONSTRAINT `user_id_fk_messages` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) + /*!40008 + ON DELETE CASCADE + ON UPDATE CASCADE */ +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -CREATE TABLE `session` ( - `sess_id` varchar(40) 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', - `ip` VARCHAR(40) NOT NULL default '', - `vars` text NOT NULL, - PRIMARY KEY (`sess_id`) -); --- -------------------------------------------------------- +-- Table structure for table `cache` --- --- Table structure for table `users` --- +CREATE TABLE `cache` ( + `cache_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `cache_key` varchar(128) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL , + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `data` longtext NOT NULL, + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY(`cache_id`), + INDEX `created_index` (`created`), + INDEX `user_cache_index` (`user_id`,`cache_key`), + CONSTRAINT `user_id_fk_cache` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) + /*!40008 + ON DELETE CASCADE + ON UPDATE CASCADE */ +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -CREATE TABLE `users` ( - `user_id` int(10) unsigned NOT NULL auto_increment, - `username` varchar(128) NOT NULL default '', - `mail_host` varchar(128) NOT NULL default '', - `alias` varchar(128) 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, - PRIMARY KEY (`user_id`), - INDEX `username_index` (`username`), - INDEX `alias_index` (`alias`) -); --- -------------------------------------------------------- +-- Table structure for table `contacts` --- --- Table structure for table `messages` --- +CREATE TABLE `contacts` ( + `contact_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `changed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `del` tinyint(1) NOT NULL DEFAULT '0', + `name` varchar(128) NOT NULL, + `email` varchar(128) NOT NULL, + `firstname` varchar(128) NOT NULL, + `surname` varchar(128) NOT NULL, + `vcard` text NULL, + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY(`contact_id`), + CONSTRAINT `user_id_fk_contacts` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) + /*!40008 + ON DELETE CASCADE + ON UPDATE CASCADE */ +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -CREATE TABLE `messages` ( - `message_id` int(11) unsigned NOT NULL auto_increment, - `user_id` int(11) unsigned NOT NULL default '0', - `del` tinyint(1) NOT NULL default '0', - `cache_key` varchar(128) NOT NULL default '', - `created` datetime NOT NULL default '0000-00-00 00:00:00', - `idx` int(11) unsigned NOT NULL default '0', - `uid` int(11) unsigned NOT NULL default '0', - `subject` varchar(255) NOT NULL default '', - `from` varchar(255) NOT NULL default '', - `to` varchar(255) NOT NULL default '', - `cc` varchar(255) NOT NULL default '', - `date` datetime NOT NULL default '0000-00-00 00:00:00', - `size` int(11) unsigned NOT NULL default '0', - `headers` text NOT NULL, - `structure` text, - PRIMARY KEY (`message_id`), - UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`) -); +-- Table structure for table `identities` +CREATE TABLE `identities` ( + `identity_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `del` tinyint(1) NOT NULL DEFAULT '0', + `standard` tinyint(1) NOT NULL DEFAULT '0', + `name` varchar(128) NOT NULL, + `organization` varchar(128) NOT NULL DEFAULT '', + `email` varchar(128) NOT NULL, + `reply-to` varchar(128) NOT NULL DEFAULT '', + `bcc` varchar(128) NOT NULL DEFAULT '', + `signature` text, + `html_signature` tinyint(1) NOT NULL DEFAULT '0', + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY(`identity_id`), + CONSTRAINT `user_id_fk_identities` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) + /*!40008 + ON DELETE CASCADE + ON UPDATE CASCADE */ +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + + +/*!40014 SET FOREIGN_KEY_CHECKS=1 */; diff --git a/SQL/mysql.update.sql b/SQL/mysql.update.sql index 2a9603f..5590099 100644 --- a/SQL/mysql.update.sql +++ b/SQL/mysql.update.sql @@ -19,9 +19,28 @@ ALTER TABLE `users` -- Updates from version 0.1.1 ALTER TABLE `identities` - MODIFY `signature` text NOT NULL DEFAULT '', + MODIFY `signature` text, MODIFY `bcc` varchar(128) NOT NULL DEFAULT '', MODIFY `reply-to` varchar(128) NOT NULL DEFAULT '', MODIFY `organization` varchar(128) NOT NULL DEFAULT '', MODIFY `name` varchar(128) NOT NULL, MODIFY `email` varchar(128) NOT NULL; + +-- Updates from version 0.2-alpha + +ALTER TABLE `messages` + ADD INDEX `created_index` (`created`); + +-- Updates from version 0.2-beta (InnoDB only) + +ALTER TABLE `cache` + DROP `session_id`; + +ALTER TABLE `session` + ADD INDEX `changed_index` (`changed`); + +ALTER TABLE `cache` + ADD INDEX `created_index` (`created`); + +ALTER TABLE `users` + CHANGE `language` `language` varchar(5); diff --git a/SQL/mysql5.initial.sql b/SQL/mysql5.initial.sql deleted file mode 100644 index 546eecd..0000000 --- a/SQL/mysql5.initial.sql +++ /dev/null @@ -1,125 +0,0 @@ --- RoundCube Webmail initial database structure --- Version 0.1 - --- -------------------------------------------------------- - -SET FOREIGN_KEY_CHECKS=0; - - --- Table structure for table `session` - -CREATE TABLE `session` ( - `sess_id` varchar(40) NOT NULL, - `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `changed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `ip` varchar(40) NOT NULL, - `vars` text NOT NULL, - PRIMARY KEY(`sess_id`) -) TYPE=INNODB CHARACTER SET utf8 COLLATE utf8_general_ci; - - --- Table structure for table `users` - -CREATE TABLE `users` ( - `user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `username` varchar(128) NOT NULL, - `mail_host` varchar(128) NOT NULL, - `alias` varchar(128) NOT NULL, - `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, - PRIMARY KEY(`user_id`), - INDEX `username_index` (`username`), - INDEX `alias_index` (`alias`) -) TYPE=INNODB CHARACTER SET utf8 COLLATE utf8_general_ci; - - --- Table structure for table `messages` - -CREATE TABLE `messages` ( - `message_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, - `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - `del` tinyint(1) NOT NULL DEFAULT '0', - `cache_key` varchar(128) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL, - `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `idx` int(11) UNSIGNED NOT NULL DEFAULT '0', - `uid` int(11) UNSIGNED NOT NULL DEFAULT '0', - `subject` varchar(255) NOT NULL, - `from` varchar(255) NOT NULL, - `to` varchar(255) NOT NULL, - `cc` varchar(255) NOT NULL, - `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `size` int(11) UNSIGNED NOT NULL DEFAULT '0', - `headers` text NOT NULL, - `structure` text, - PRIMARY KEY(`message_id`), - UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`), - CONSTRAINT `user_id_fk_messages` FOREIGN KEY (`user_id`) - REFERENCES `users`(`user_id`) - ON DELETE CASCADE - ON UPDATE CASCADE -) TYPE=INNODB CHARACTER SET utf8 COLLATE utf8_general_ci; - - --- Table structure for table `cache` - -CREATE TABLE `cache` ( - `cache_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `session_id` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci, - `cache_key` varchar(128) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL, - `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `data` longtext NOT NULL, - `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - PRIMARY KEY(`cache_id`), - INDEX `user_cache_index` (`user_id`,`cache_key`), - CONSTRAINT `user_id_fk_cache` FOREIGN KEY (`user_id`) - REFERENCES `users`(`user_id`) - ON DELETE CASCADE - ON UPDATE CASCADE -) TYPE=INNODB CHARACTER SET utf8 COLLATE utf8_general_ci; - - --- Table structure for table `contacts` - -CREATE TABLE `contacts` ( - `contact_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `changed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `del` tinyint(1) NOT NULL DEFAULT '0', - `name` varchar(128) NOT NULL, - `email` varchar(128) NOT NULL, - `firstname` varchar(128) NOT NULL, - `surname` varchar(128) NOT NULL, - `vcard` text NULL, - `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - PRIMARY KEY(`contact_id`), - CONSTRAINT `user_id_fk_contacts` FOREIGN KEY (`user_id`) - REFERENCES `users`(`user_id`) - ON DELETE CASCADE - ON UPDATE CASCADE -) TYPE=INNODB CHARACTER SET utf8 COLLATE utf8_general_ci; - - --- Table structure for table `identities` - -CREATE TABLE `identities` ( - `identity_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `del` tinyint(1) NOT NULL DEFAULT '0', - `standard` tinyint(1) NOT NULL DEFAULT '0', - `name` varchar(128) NOT NULL, - `organization` varchar(128) NOT NULL DEFAULT '', - `email` varchar(128) NOT NULL, - `reply-to` varchar(128) NOT NULL DEFAULT '', - `bcc` varchar(128) NOT NULL DEFAULT '', - `signature` text NOT NULL DEFAULT '', - `html_signature` tinyint(1) NOT NULL DEFAULT '0', - `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - PRIMARY KEY(`identity_id`), - CONSTRAINT `user_id_fk_identities` FOREIGN KEY (`user_id`) - REFERENCES `users`(`user_id`) - ON DELETE CASCADE - ON UPDATE CASCADE -) TYPE=INNODB CHARACTER SET utf8 COLLATE utf8_general_ci; - - -SET FOREIGN_KEY_CHECKS=1; diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql index 05c6192..eb53332 100644 --- a/SQL/postgres.initial.sql +++ b/SQL/postgres.initial.sql @@ -21,7 +21,7 @@ CREATE TABLE users ( alias character varying(128) DEFAULT ''::character varying NOT NULL, created timestamp with time zone DEFAULT now() NOT NULL, last_login timestamp with time zone DEFAULT now() NOT NULL, - "language" character varying(5) DEFAULT 'en'::character varying NOT NULL, + "language" character varying(5), preferences text DEFAULT ''::text NOT NULL ); @@ -42,6 +42,7 @@ CREATE TABLE "session" ( vars text NOT NULL ); +CREATE INDEX session_changed_idx ON session (changed); -- @@ -64,8 +65,8 @@ CREATE SEQUENCE identity_ids CREATE TABLE identities ( identity_id integer DEFAULT nextval('identity_ids'::text) PRIMARY KEY, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, - del integer DEFAULT 0 NOT NULL, - standard integer DEFAULT 0 NOT NULL, + del smallint DEFAULT 0 NOT NULL, + standard smallint DEFAULT 0 NOT NULL, name character varying(128) NOT NULL, organization character varying(128), email character varying(128) NOT NULL, @@ -99,7 +100,7 @@ CREATE TABLE contacts ( contact_id integer DEFAULT nextval('contact_ids'::text) PRIMARY KEY, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, changed timestamp with time zone DEFAULT now() NOT NULL, - del integer DEFAULT 0 NOT NULL, + del smallint DEFAULT 0 NOT NULL, name character varying(128) DEFAULT ''::character varying NOT NULL, email character varying(128) DEFAULT ''::character varying NOT NULL, firstname character varying(128) DEFAULT ''::character varying NOT NULL, @@ -128,13 +129,13 @@ CREATE SEQUENCE cache_ids CREATE TABLE "cache" ( cache_id integer DEFAULT nextval('cache_ids'::text) PRIMARY KEY, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, - session_id character varying(40) REFERENCES "session" (sess_id), cache_key character varying(128) DEFAULT ''::character varying NOT NULL, created timestamp with time zone DEFAULT now() NOT NULL, data text NOT NULL ); CREATE INDEX cache_user_id_idx ON "cache" (user_id, cache_key); +CREATE INDEX cache_created_idx ON "cache" (created); -- -- Sequence "message_ids" @@ -152,10 +153,10 @@ CREATE SEQUENCE message_ids -- Name: messages; Type: TABLE; Schema: public; Owner: postgres -- -CREATE TABLE "messages" ( +CREATE TABLE messages ( message_id integer DEFAULT nextval('message_ids'::text) PRIMARY KEY, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, - del integer DEFAULT 0 NOT NULL, + del smallint DEFAULT 0 NOT NULL, cache_key character varying(128) DEFAULT ''::character varying NOT NULL, created timestamp with time zone DEFAULT now() NOT NULL, idx integer DEFAULT 0 NOT NULL, @@ -170,4 +171,5 @@ CREATE TABLE "messages" ( structure text ); -ALTER TABLE "messages" ADD UNIQUE (user_id, cache_key, uid); +ALTER TABLE messages ADD UNIQUE (user_id, cache_key, uid); +CREATE INDEX messages_created_idx ON messages (created); diff --git a/SQL/postgres.update.sql b/SQL/postgres.update.sql index e0afa61..a29558e 100644 --- a/SQL/postgres.update.sql +++ b/SQL/postgres.update.sql @@ -17,3 +17,22 @@ ALTER TABLE contacts DROP CONSTRAINT contacts_user_id_fkey; ALTER TABLE contacts ADD FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE cache DROP CONSTRAINT cache_user_id_fkey; ALTER TABLE cache ADD FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE; + +-- Updates from version 0.2-alpha + +CREATE INDEX messages_created_idx ON messages (created); + +-- Updates from version 0.2-beta + +ALTER TABLE cache DROP session_id; + +CREATE INDEX session_changed_idx ON session (changed); +CREATE INDEX cache_created_idx ON "cache" (created); + +ALTER TABLE users ALTER "language" DROP NOT NULL; +ALTER TABLE users ALTER "language" DROP DEFAULT; + +ALTER TABLE identities ALTER del TYPE smallint; +ALTER TABLE identities ALTER standard TYPE smallint; +ALTER TABLE contacts ALTER del TYPE smallint; +ALTER TABLE messages ALTER del TYPE smallint; diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql index 6adcf4d..5120ea0 100644 --- a/SQL/sqlite.initial.sql +++ b/SQL/sqlite.initial.sql @@ -11,13 +11,13 @@ CREATE TABLE cache ( cache_id integer NOT NULL PRIMARY KEY, user_id integer NOT NULL default 0, - session_id varchar(40) default NULL, cache_key varchar(128) NOT NULL default '', created datetime NOT NULL default '0000-00-00 00:00:00', data longtext NOT NULL ); CREATE INDEX ix_cache_user_cache_key ON cache(user_id, cache_key); +CREATE INDEX ix_cache_created ON cache(created); -- -------------------------------------------------------- @@ -76,7 +76,7 @@ CREATE TABLE users ( alias varchar(128) 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', + language varchar(5), preferences text NOT NULL default '' ); @@ -97,6 +97,7 @@ CREATE TABLE session ( vars text NOT NULL ); +CREATE INDEX ix_session_changed ON session (changed); -- -------------------------------------------------------- @@ -123,3 +124,4 @@ CREATE TABLE messages ( ); CREATE INDEX ix_messages_user_cache_uid ON messages(user_id,cache_key,uid); +CREATE INDEX ix_messages_created ON messages (created); diff --git a/SQL/sqlite.update.sql b/SQL/sqlite.update.sql index 047fe67..627074e 100644 --- a/SQL/sqlite.update.sql +++ b/SQL/sqlite.update.sql @@ -25,3 +25,12 @@ CREATE INDEX ix_messages_user_cache_uid ON messages(user_id,cache_key,uid); CREATE INDEX ix_users_username ON users(username); CREATE INDEX ix_users_alias ON users(alias); + +-- Updates from version 0.2-alpha + +CREATE INDEX ix_messages_created ON messages (created); + +-- Updates from version 0.2-beta + +CREATE INDEX ix_session_changed ON session (changed); +CREATE INDEX ix_cache_created ON cache (created); diff --git a/UPGRADING b/UPGRADING index 996b4f2..54a4ca3 100644 --- a/UPGRADING +++ b/UPGRADING @@ -1,5 +1,5 @@ -UPDATE instructions -=================== +UPGRADING instructions +====================== First you should remove all subfolders from /program/localization/ because most language codes have changed in 0.2-alpha. This way you @@ -8,6 +8,42 @@ can make sure that no old localization files remain on your disk. Then follow these instructions if upgrading from a previous version of RoundCube Webmail. +1. Replace index.php and all files in + - ./bin/ + - ./SQL/ + - ./program/ + - ./installer/ + - ./skins/default/ +2. Run ./bin/update.sh from the commandline OR + open http://url-to-roundcube/installer/ in a browser. To enable + the latter one, you have to temporary set 'enable_installer' to true + in your local config/main.inc.php file. +3. Let the update script/installer check your configuration and + update your config files as suggested by the updater. +4. Run all commands in ./SQL/[yourdbtype].update.sql that are superscribed + with the currently installed version number. +5. Make sure 'enable_installer' is set to false again. + + +For manually upgrading your RoundCube installation follow the instructions +that match the currently installed version: + +from version 0.2-alpha +---------------------------------------- +* replace index.php +* replace all files in folder /bin/ +* replace all files in folder /program/ +* replace all files in folder /installer/ +* replace all files in folder /skins/default/ +* run all commands in SQL/[yourdbtype].update.sql + below the line "-- Updates from version 0.2-alpha" +* check the config/main.inc.php.dist for new configuration + options and add them to your config + WARNING: 'skin_path' option was replaced by 'skin' option +* WARNING: 'db_backend' option has been removed, now only + PEAR::MDB2 driver is supported + + from version 0.1.1 ---------------------------------------- * replace index.php diff --git a/bin/html2text.php b/bin/html2text.php index 0f0e6ae..b245b31 100644 --- a/bin/html2text.php +++ b/bin/html2text.php @@ -1,11 +1,38 @@ | + +-----------------------------------------------------------------------+ -$converter = new html2text($HTTP_RAW_POST_DATA); + $Id: html2text.php 2187 2008-12-24 14:19:27Z thomasb $ -header('Content-Type: text/plain; charset=UTF-8'); -print html_entity_decode($converter->get_text(), ENT_COMPAT, 'UTF-8'); +*/ + +define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/'); +require INSTALL_PATH . 'program/include/iniset.php'; + +$RCMAIL = rcmail::get_instance(); + +if (!empty($RCMAIL->user->ID)) { + $converter = new html2text($HTTP_RAW_POST_DATA); + + header('Content-Type: text/plain; charset=UTF-8'); + print trim($converter->get_text()); +} +else { + header("HTTP/1.0 403 Forbidden"); + echo "Requires a valid user session"; +} ?> diff --git a/bin/killcache.php b/bin/killcache.php new file mode 100644 index 0000000..8951eb4 --- /dev/null +++ b/bin/killcache.php @@ -0,0 +1,56 @@ + | + +-----------------------------------------------------------------------+ + + $Id: killcache.php 1955 2008-10-07 19:11:06Z alec $ + +*/ + +define('INSTALL_PATH', realpath(dirname(__FILE__).'/..') . '/'); +require INSTALL_PATH.'program/include/iniset.php'; + +$config = new rcube_config(); + +// don't allow public access if not in devel_mode +if (!$config->get('devel_mode') && $_SERVER['REMOTE_ADDR']) { + header("HTTP/1.0 401 Access denied"); + die("Access denied!"); +} + + +$dbh =& MDB2::factory($config->get('db_dsnw'), $options); +if (PEAR::isError($dbh)) { + exit($mdb2->getMessage()); +} + +//TODO: transaction here (if supported by DB) would be a good thing +$res =& $dbh->exec("DELETE FROM cache"); +if (PEAR::isError($res)) { + $dbh->disconnect(); + exit($res->getMessage()); +}; + +$res =& $dbh->exec("DELETE FROM messages"); +if (PEAR::isError($res)) { + $dbh->disconnect(); + exit($res->getMessage()); +}; + +echo "Cache cleared\n"; + +$dbh->disconnect(); + +?> diff --git a/bin/modcss.php b/bin/modcss.php index e97b8ec..b56accb 100644 --- a/bin/modcss.php +++ b/bin/modcss.php @@ -2,10 +2,10 @@ /* +-----------------------------------------------------------------------+ - | program/bin/modcss.php | + | bin/modcss.php | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2007, RoundCube Dev. - Switzerland | + | Copyright (C) 2007-2008, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -15,15 +15,17 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: $ + $Id: modcss.php 2187 2008-12-24 14:19:27Z thomasb $ */ -define('INSTALL_PATH', realpath('./../') . '/'); -require INSTALL_PATH.'program/include/iniset.php'; +define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/'); +require INSTALL_PATH . 'program/include/iniset.php'; + +$RCMAIL = rcmail::get_instance(); $source = ""; -if ($url = preg_replace('/[^a-z0-9.-_\?\$&=%]/i', '', $_GET['u'])) +if (!empty($RCMAIL->user->ID) && ($url = preg_replace('/[^a-z0-9.-_\?\$&=%]/i', '', $_GET['u']))) { $a_uri = parse_url($url); $port = $a_uri['port'] ? $a_uri['port'] : 80; @@ -59,7 +61,9 @@ if (!empty($source)) header("Content-Type: text/css"); echo rcmail_mod_css_styles($source, preg_replace('/[^a-z0-9]/i', '', $_GET['c']), $url); } -else +else { header("HTTP/1.0 404 Not Found"); + echo "Requires a valid user session and source url"; +} ?> diff --git a/bin/msgexport.sh b/bin/msgexport.sh index 890d48a..b15da1f 100755 --- a/bin/msgexport.sh +++ b/bin/msgexport.sh @@ -1,7 +1,7 @@ -#!/usr/bin/php -qC +#!/usr/bin/php ', $pos, $max)); } +function export_mailbox($mbox, $filename) +{ + global $IMAP; + + $IMAP->set_mailbox($mbox); + + vputs("Getting message list of {$mbox}..."); + vputs($IMAP->messagecount()." messages\n"); + + if ($filename) + { + if (!($out = fopen($filename, 'w'))) + { + vputs("Cannot write to output file\n"); + return; + } + vputs("Writing to $filename\n"); + } + else + $out = STDOUT; + + for ($count = $IMAP->messagecount(), $i=1; $i <= $count; $i++) + { + $headers = $IMAP->get_headers($i, null, false); + $from = current($IMAP->decode_address_list($headers->from, 1, false)); + + fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid)); + fwrite($out, iil_C_FetchPartHeader($IMAP->conn, $IMAP->mailbox, $i, null)); + fwrite($out, iil_C_HandlePartBody($IMAP->conn, $IMAP->mailbox, $i, null, 1)); + fwrite($out, "\n\n\n"); + + progress_update($i, $count); + } + vputs("\ncomplete.\n"); + + if ($filename) + fclose($out); +} + // get arguments $args = get_opt(array('h' => 'host', 'u' => 'user', 'p' => 'pass', 'm' => 'mbox', 'f' => 'file')) + array('host' => 'localhost', 'mbox' => 'INBOX'); @@ -109,35 +148,21 @@ if ($IMAP->connect($host, $args['user'], $args['pass'], $imap_port, $imap_ssl)) { vputs("IMAP login successful.\n"); - $IMAP->set_mailbox($args['mbox']); - - vputs("Getting message list of {$args['mbox']}..."); - vputs($IMAP->messagecount()." messages\n"); - - if ($args['file']) - { - if (!($out = fopen($args['file'], 'w'))) - { - vputs("Cannot write to output file\n"); - exit; - } - } - else - $out = STDOUT; - - for ($count = $IMAP->messagecount(), $i=1; $i <= $count; $i++) + $filename = null; + $mailboxes = $args['mbox'] == '*' ? $IMAP->list_mailboxes(null) : array($args['mbox']); + + foreach ($mailboxes as $mbox) { - $headers = $IMAP->get_headers($i, null, false); - $from = current($IMAP->decode_address_list($headers->from, 1, false)); - - fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid)); - fwrite($out, iil_C_FetchPartHeader($IMAP->conn, $IMAP->mailbox, $i, null)); - fwrite($out, iil_C_HandlePartBody($IMAP->conn, $IMAP->mailbox, $i, null, 1)); - fwrite($out, "\n\n\n"); - - progress_update($i, $count); + if ($args['file']) + $filename = preg_replace('/\.[a-z0-9]{3,4}$/i', '', $args['file']) . asciiwords($mbox) . '.mbox'; + else if ($args['mbox'] == '*') + $filename = asciiwords($mbox) . '.mbox'; + + if ($args['mbox'] == '*' && in_array(strtolower($mbox), array('junk','spam','trash'))) + continue; + + export_mailbox($mbox, $filename); } - vputs("\ncomplete.\n"); } else { diff --git a/bin/msgimport.sh b/bin/msgimport.sh index 98a0389..fa5678c 100755 --- a/bin/msgimport.sh +++ b/bin/msgimport.sh @@ -1,7 +1,7 @@ -#!/usr/bin/php -qC +#!/usr/bin/php 'host', 'u' => 'user', 'p' => 'pass', 'f' => 'file')) + array('host' => 'localhost'); +$args = get_opt(array('h' => 'host', 'u' => 'user', 'p' => 'pass', 'm' => 'mbox', 'f' => 'file')) + array('host' => 'localhost', 'mbox' => 'INBOX'); if ($_SERVER['argv'][1] == 'help') { @@ -77,11 +77,14 @@ if (empty($args['user'])) } // prompt for password -echo "Password: "; -$args['pass'] = trim(fgets(STDIN)); +if (empty($args['pass'])) +{ + echo "Password: "; + $args['pass'] = trim(fgets(STDIN)); -// clear password input -echo chr(8)."\rPassword: ".str_repeat("*", strlen($args['pass']))."\n"; + // clear password input + echo chr(8)."\rPassword: ".str_repeat("*", strlen($args['pass']))."\n"; +} // parse $host URL $a_host = parse_url($args['host']); @@ -104,13 +107,39 @@ $IMAP = new rcube_imap(null); if ($IMAP->connect($host, $args['user'], $args['pass'], $imap_port, $imap_ssl)) { print "IMAP login successful.\n"; - print "Uploading message...\n"; + print "Uploading messages...\n"; + $count = 0; + $message = $lastline = ''; + + $fp = fopen($args['file'], 'r'); + while (($line = fgets($fp)) !== false) + { + if (preg_match('/^From\s+/', $line) && $lastline == '') + { + if (!empty($message)) + { + if ($IMAP->save_message($args['mbox'], rtrim($message))) + $count++; + else + die("Failed to save message to {$args['mbox']}\n"); + $message = ''; + } + continue; + } + + $message .= $line; + $lastline = rtrim($line); + } + + if (!empty($message) && $IMAP->save_message($args['mbox'], rtrim($message))) + $count++; + // upload message from file - if ($IMAP->save_message('INBOX', file_get_contents($args['file']))) - print "Message successfully added to INBOX.\n"; + if ($count) + print "$count messages successfully added to {$args['mbox']}.\n"; else - print "Adding message failed!\n"; + print "Adding messages failed!\n"; } else { diff --git a/bin/quotaimg.php b/bin/quotaimg.php index 354f4eb..6e3d7de 100644 --- a/bin/quotaimg.php +++ b/bin/quotaimg.php @@ -1,10 +1,10 @@ | +-----------------------------------------------------------------------+ - $Id: $ + $Id: quotaimg.php 2187 2008-12-24 14:19:27Z thomasb $ */ -$used = ((isset($_GET['u']) && !empty($_GET['u'])) || $_GET['u']=='0')?(int)$_GET['u']:'??'; -$quota = ((isset($_GET['q']) && !empty($_GET['q'])) || $_GET['q']=='0')?(int)$_GET['q']:'??'; -$width = empty($_GET['w']) ? 100 : (int)$_GET['w']; -$height = empty($_GET['h']) ? 14 : (int)$_GET['h']; +define('INSTALL_PATH', realpath(dirname(__FILE__).'/..') . '/'); +require INSTALL_PATH . 'program/include/iniset.php'; + +$RCMAIL = rcmail::get_instance(); + +$used = isset($_GET['u']) ? intval($_GET['u']) : '??'; +$quota = isset($_GET['q']) ? intval($_GET['q']) : '??'; +$width = empty($_GET['w']) ? 100 : min(300, intval($_GET['w'])); +$height = empty($_GET['h']) ? 14 : min(50, intval($_GET['h'])); /** * Quota display @@ -70,14 +75,14 @@ function genQuota($used, $total, $width, $height) $font = 2; $padding = 0; - $limit['high'] = 70; - $limit['mid'] = 45; + $limit['high'] = 80; + $limit['mid'] = 55; $limit['low'] = 0; // Fill Colors - $color['fill']['high'] = '215, 13, 13'; // Near quota fill color - $color['fill']['mid'] = '126, 192, 238'; // Mid-area of quota fill color - $color['fill']['low'] = '147, 225, 100'; // Far from quota fill color + $color['fill']['high'] = '243, 49, 49'; // Near quota fill color + $color['fill']['mid'] = '245, 173, 60'; // Mid-area of quota fill color + $color['fill']['low'] = '145, 225, 100'; // Far from quota fill color // Background colors $color['bg']['OL'] = '215, 13, 13'; // Over limit bbackground @@ -86,22 +91,24 @@ function genQuota($used, $total, $width, $height) // Misc. Colors $color['border'] = '0, 0, 0'; - $color['text'] = '102, 102, 102'; + $color['text']['high'] = '255, 255, 255'; // white text for red background + $color['text']['mid'] = '102, 102, 102'; + $color['text']['low'] = '102, 102, 102'; + $color['text']['normal'] = '102, 102, 102'; /************************************ ***** DO NOT EDIT BELOW HERE ***** ***********************************/ - // @todo: Set to "??" instead? + // @todo: Set to "??" instead? if (ereg("^[^0-9?]*$", $used) || ereg("^[^0-9?]*$", $total)) { return false; - } + } - if (strpos($used, '?') !== false || strpos($total, '?') !== false - && $used != 0) { + if (strpos($used, '?') !== false || strpos($total, '?') !== false && $used != 0) { $unknown = true; - } + } $im = imagecreate($width, $height); @@ -116,20 +123,21 @@ function genQuota($used, $total, $width, $height) imageline($im, $width, $height-$border, 0, $height-$border, $borderc); } - list($r, $g, $b) = explode(',', $color['text']); - $text = imagecolorallocate($im, $r, $g, $b); - if ($unknown) { + list($r, $g, $b) = explode(',', $color['text']['normal']); + $text = imagecolorallocate($im, $r, $g, $b); list($r, $g, $b) = explode(',', $color['bg']['Unknown']); $background = imagecolorallocate($im, $r, $g, $b); + imagefilledrectangle($im, 0, 0, $width, $height, $background); $string = 'Unknown'; $mid = floor(($width-(strlen($string)*imagefontwidth($font)))/2)+1; imagestring($im, $font, $mid, $padding, $string, $text); } else if ($used > $total) { + list($r, $g, $b) = explode(',', $color['text']['normal']); + $text = imagecolorallocate($im, $r, $g, $b); list($r, $g, $b) = explode(',', $color['bg']['OL']); - $background = imagecolorallocate($im, $r, $g, $b); imagefilledrectangle($im, 0, 0, $width, $height, $background); @@ -139,7 +147,6 @@ function genQuota($used, $total, $width, $height) imagestring($im, $font, $mid, $padding, $string, $text); } else { list($r, $g, $b) = explode(',', $color['bg']['quota']); - $background = imagecolorallocate($im, $r, $b, $g); imagefilledrectangle($im, 0, 0, $width, $height, $background); @@ -147,37 +154,50 @@ function genQuota($used, $total, $width, $height) $quota = ($used==0)?0:(round($used/$total, 2)*100); if ($quota >= $limit['high']) { + list($r, $g, $b) = explode(',', $color['text']['high']); + $text = imagecolorallocate($im, $r, $g, $b); list($r, $g, $b) = explode(',', $color['fill']['high']); $fill = imagecolorallocate($im, $r, $g, $b); } elseif($quota >= $limit['mid']) { + list($r, $g, $b) = explode(',', $color['text']['mid']); + $text = imagecolorallocate($im, $r, $g, $b); list($r, $g, $b) = explode(',', $color['fill']['mid']); $fill = imagecolorallocate($im, $r, $g, $b); } else { - // if($quota >= $limit['low']) + // if($quota >= $limit['low']) + list($r, $g, $b) = explode(',', $color['text']['low']); + $text = imagecolorallocate($im, $r, $g, $b); list($r, $g, $b) = explode(',', $color['fill']['low']); $fill = imagecolorallocate($im, $r, $g, $b); } $quota_width = $quota / 100 * $width; - imagefilledrectangle($im, $border, 0, $quota, $height-2*$border, $fill); + imagefilledrectangle($im, $border, 0, $quota_width, $height-2*$border, $fill); $string = $quota . '%'; $mid = floor(($width-(strlen($string)*imagefontwidth($font)))/2)+1; - // Print percent in black + // Print percent in black imagestring($im, $font, $mid, $padding, $string, $text); } header('Content-Type: image/gif'); - - // @todo is harcoding GMT necessary? - header('Expires: ' . gmdate('D, d M Y H:i:s', mktime()+86400) . ' GMT'); - header('Cache-Control: '); - header('Pragma: '); + + // cache for 1 hour + $maxage = 3600; + header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$maxage). ' GMT'); + header('Cache-Control: max-age=' . $maxage); imagegif($im); imagedestroy($im); } -genQuota($used, $quota, $width, $height); +if (!empty($RCMAIL->user->ID) && $width > 1 && $height > 1) { + genQuota($used, $quota, $width, $height); +} +else { + header("HTTP/1.0 403 Forbidden"); + echo "Requires a valid user session and positive values"; +} + exit; -?> \ No newline at end of file +?> diff --git a/bin/update.sh b/bin/update.sh new file mode 100755 index 0000000..a9a917c --- /dev/null +++ b/bin/update.sh @@ -0,0 +1,115 @@ +#!/usr/bin/php +load_config(); + +if ($RCI->configured) { + if ($messages = $RCI->check_config()) { + $err = 0; + + // list missing config options + if (is_array($messages['missing'])) { + echo "WARNING: Missing config options:\n"; + echo "(These config options should be present in the current configuration)\n"; + + foreach ($messages['missing'] as $msg) { + echo "- '" . $msg['prop'] . ($msg['name'] ? "': " . $msg['name'] : "'") . "\n"; + $err++; + } + echo "\n"; + } + + // list old/replaced config options + if (is_array($messages['replaced'])) { + echo "WARNING: Replaced config options:\n"; + echo "(These config options have been replaced or renamed)\n"; + + foreach ($messages['replaced'] as $msg) { + echo "- '" . $msg['prop'] . "' was replaced by '" . $msg['replacement'] . "'\n"; + $err++; + } + echo "\n"; + } + + // list obsolete config options (just a notice) + if (is_array($messages['obsolete'])) { + echo "NOTICE: Obsolete config options:\n"; + echo "(You still have some obsolete or inexistent properties set. This isn't a problem but should be noticed)\n"; + + foreach ($messages['obsolete'] as $msg) { + echo "- '" . $msg['prop'] . ($msg['name'] ? "': " . $msg['name'] : "'") . "\n"; + $err++; + } + echo "\n"; + } + + // ask user to update config files + if ($err) { + echo "Do you want me to fix your local configuration? (y/N)\n"; + $input = trim(fgets(STDIN)); + + // positive: let's merge the local config with the defaults + if (strtolower($input) == 'y') { + $copy1 = $copy2 = $write1 = $write2 = false; + + // backup current config + echo ". backing up the current config files...\n"; + $copy1 = copy(RCMAIL_CONFIG_DIR . '/main.inc.php', RCMAIL_CONFIG_DIR . '/main.old.php'); + $copy2 = copy(RCMAIL_CONFIG_DIR . '/db.inc.php', RCMAIL_CONFIG_DIR . '/db.old.php'); + + if ($copy1 && $copy2) { + $RCI->merge_config(); + + echo ". writing " . RCMAIL_CONFIG_DIR . "/main.inc.php...\n"; + $write1 = file_put_contents(RCMAIL_CONFIG_DIR . '/main.inc.php', $RCI->create_config('main', true)); + echo ". writing " . RCMAIL_CONFIG_DIR . "/main.db.php...\n"; + $write2 = file_put_contents(RCMAIL_CONFIG_DIR . '/db.inc.php', $RCI->create_config('db', true)); + } + + // Success! + if ($write1 && $write2) { + echo "Done.\n"; + echo "Your configuration files are now up-tp-date!\n"; + } + else { + echo "Failed to write config files!\n"; + echo "Grant write privileges to the current user or update the files manually according to the above messages.\n"; + } + } + else { + echo "Please update your config files manually according to the above messages.\n"; + } + } + + // check dependencies based on the current configuration + if (is_array($messages['dependencies'])) { + echo "WARNING: Dependency check failed!\n"; + echo "(Some of your configuration settings require other options to be configured or additional PHP modules to be installed)\n"; + + foreach ($messages['dependencies'] as $msg) { + echo "- " . $msg['prop'] . ': ' . $msg['explain'] . "\n"; + } + echo "Please fix your config files and run this script again!\n"; + echo "See ya.\n"; + } + + } + else { + echo "This instance of RoundCube is up-to-date.\n"; + echo "Have fun!\n"; + } +} +else { + echo "This instance of RoundCube is not yet configured!\n"; + echo "Open http://url-to-roundcube/installer/ in your browser and follow the instuctions.\n"; +} + +echo "\n"; + +?> \ No newline at end of file diff --git a/config/db.inc.php.dist b/config/db.inc.php.dist index 151439a..255fb3e 100644 --- a/config/db.inc.php.dist +++ b/config/db.inc.php.dist @@ -26,9 +26,6 @@ $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail'; // useful for database replication $rcmail_config['db_dsnr'] = ''; -// database backend to use (only db or mdb2 are supported) -$rcmail_config['db_backend'] = 'mdb2'; - // maximum length of a query in bytes $rcmail_config['db_max_length'] = 512000; // 500K diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 67d82c8..f8d9b29 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -18,6 +18,23 @@ $rcmail_config = array(); // system error reporting: 1 = log; 2 = report (not implemented yet), 4 = show, 8 = trace $rcmail_config['debug_level'] = 1; +// log driver: 'syslog' or 'file'. +$rcmail_config['log_driver'] = 'file'; + +// Syslog ident string to use, if using the 'syslog' log driver. +$rcmail_config['syslog_id'] = 'roundcube'; + +// Syslog facility to use, if using the 'syslog' log driver. +// For possible values see installer or http://php.net/manual/en/function.openlog.php +$rcmail_config['syslog_facility'] = LOG_USER; + +// use this folder to store log files (must be writeable for apache user) +// This is used by the 'file' log driver. +$rcmail_config['log_dir'] = 'logs/'; + +// use this folder to store temp files (must be writeable for apache user) +$rcmail_config['temp_dir'] = 'temp/'; + // enable caching of messages and mailbox data in the local database. // this is recommended if the IMAP server does not run on the same machine $rcmail_config['enable_caching'] = TRUE; @@ -44,6 +61,11 @@ $rcmail_config['default_port'] = 143; // Optional, defaults to "check" $rcmail_config['imap_auth_type'] = null; +// If you know your imap's root directory and its folder delimiter, +// you can specify them here. Otherwise they will be determined automatically. +$rcmail_config['imap_root'] = null; +$rcmail_config['imap_delimiter'] = null; + // Automatically add this domain to user names for login // Only for IMAP servers that require full e-mail addresses for login // Specify an array with 'host' => 'domain' values to support multiple hosts @@ -90,23 +112,18 @@ $rcmail_config['smtp_helo_host'] = ''; // 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'); +// How many seconds must pass between emails sent by a user +$rcmail_config['sendmail_delay'] = 0; -// relative path to the skin folder -$rcmail_config['skin_path'] = 'skins/default/'; +// These cols are shown in the message list. Available cols are: +// subject, from, to, cc, replyto, date, size, flag, attachment +$rcmail_config['list_cols'] = array('subject', 'from', 'date', 'size', 'flag', 'attachment'); -// includes should be interpreted as PHP files +// Includes should be interpreted as PHP files $rcmail_config['skin_include_php'] = FALSE; -// use this folder to store temp files (must be writebale for apache user) -$rcmail_config['temp_dir'] = 'temp/'; - -// use this folder to store log files (must be writebale for apache user) -$rcmail_config['log_dir'] = 'logs/'; - -// session lifetime in minutes +// Session lifetime in minutes +// must be greater than 'keep_alive'/60 $rcmail_config['session_lifetime'] = 10; // check client IP in session athorization @@ -121,8 +138,9 @@ $rcmail_config['double_auth'] = false; // please provide a string of exactly 24 chars. $rcmail_config['des_key'] = 'rcmail-!24ByteDESkey*Str'; -// the default locale setting -$rcmail_config['language'] = 'en'; +// the default locale setting (leave empty for auto-detection) +// RFC1766 formatted language name like en_US, de_DE, de_CH, fr_FR, pt_BR +$rcmail_config['language'] = null; // use this format for short date display $rcmail_config['date_short'] = 'D H:i'; @@ -134,14 +152,11 @@ $rcmail_config['date_long'] = 'd.m.Y H:i'; $rcmail_config['date_today'] = 'H:i'; // add this user-agent to message headers when sending -$rcmail_config['useragent'] = 'RoundCube Webmail/0.2a'; +$rcmail_config['useragent'] = 'RoundCube Webmail/0.2'; // use this name to compose page titles $rcmail_config['product_name'] = 'RoundCube Webmail'; -// only list folders within this path -$rcmail_config['imap_root'] = ''; - // store draft message is this mailbox // leave blank if draft messages should not be stored $rcmail_config['drafts_mbox'] = 'Drafts'; @@ -167,18 +182,8 @@ $rcmail_config['create_default_folders'] = FALSE; // protect the default folders from renames, deletes, and subscription changes $rcmail_config['protect_default_folders'] = TRUE; -// Set TRUE if deleted messages should not be displayed -// This will make the application run slower -$rcmail_config['skip_deleted'] = FALSE; - -// Set true to Mark deleted messages as read as well as deleted -// False means that a message's read status is not affected by marking it as deleted -$rcmail_config['read_when_deleted'] = TRUE; - -// When a Trash folder is not present and a message is deleted, flag -// the message for deletion rather than deleting it immediately. Setting this to -// false causes deleted messages to be permanantly removed if there is no Trash folder -$rcmail_config['flag_for_deletion'] = TRUE; +// if in your system 0 quota means no limit set this option to TRUE +$rcmail_config['quota_zero_as_unlimited'] = FALSE; // Behavior if a received message requests a message delivery notification (read receipt) // 0 = ask the user, 1 = send automatically, 2 = ignore (never send or ask) @@ -192,6 +197,10 @@ $rcmail_config['default_charset'] = 'ISO-8859-1'; // requires to be compiled with Open SSL support $rcmail_config['enable_spellcheck'] = TRUE; +// Set the spell checking engine. 'googie' is the default. 'pspell' is also available, +// but requires the Pspell extensions. When using Nox Spell Server, also set 'googie' here. +$rcmail_config['spellcheck_engine'] = 'googie'; + // For a locally installed Nox Spell Server, please specify the URI to call it. // Get Nox Spell Server from http://orangoo.com/labs/?page_id=72 // Leave empty to use the Google spell checking service, what means @@ -200,7 +209,8 @@ $rcmail_config['spellcheck_uri'] = ''; // These languages can be selected for spell checking. // Configure as a PHP style hash array: array('en'=>'English', 'de'=>'Deutsch'); -// Leave empty for default set of Google spell check languages +// Leave empty for default set of Google spell check languages, should be defined +// when using local Pspell extension $rcmail_config['spellcheck_languages'] = NULL; // path to a text file which will be added to each sent message @@ -226,6 +236,8 @@ $rcmail_config['address_book_type'] = 'sql'; // In order to enable public ldap search, configure an array like the Verisign // example further below. if you would like to test, simply uncomment the example. +$rcmail_config['ldap_public'] = array(); + // // If you are going to use LDAP for individual address books, you will need to // set 'user_specific' to true and use the variables to generate the appropriate DNs to access it. @@ -271,13 +283,23 @@ $rcmail_config['address_book_type'] = 'sql'; * 'sort' => 'cn', // The field to sort the listing by. * 'scope' => 'sub', // search mode: sub|base|list * 'filter' => '', // used for basic listing (if not empty) and will be &'d with search queries. example: status=act - * 'global_search' => true, // perform a global search for address auto-completion on compose * 'fuzzy_search' => true); // server allows wildcard search */ +// An ordered array of the ids of the addressbooks that should be searched +// when populating address autocomplete fields server-side. ex: array('sql','Verisign'); +$rcmail_config['autocomplete_addressbooks'] = array('sql'); + // don't allow these settings to be overriden by the user $rcmail_config['dont_override'] = array(); +// Set identities access level: +// 0 - many identities with possibility to edit all params +// 1 - many identities with possibility to edit all params but not email address +// 2 - one identity with possibility to edit all params +// 3 - one identity with possibility to edit all params but not email address +$rcmail_config['identities_level'] = 0; + // try to load host-specific configuration // see http://trac.roundcube.net/wiki/Howto_Config for more details $rcmail_config['include_host_config'] = false; @@ -298,13 +320,35 @@ $rcmail_config['message_sort_order'] = 'DESC'; // ONLY ENABLE IT IF YOU'RE REALLY SURE WHAT YOU'RE DOING! $rcmail_config['enable_installer'] = false; +// Log successful logins +$rcmail_config['log_logins'] = false; + +/** + * 'Delete always' + * This setting reflects if mail should be always marked as deleted, + * even if moving to "Trash" fails. This is necessary in some setups + * because a) people may not have a Trash folder or b) they are over + * quota (and Trash is included in the quota). + * + * This is a failover setting for iil_C_Move when a message is moved + * to the Trash. + */ +$rcmail_config['delete_always'] = false; + +// Minimal value of user's 'keep_alive' setting (in seconds) +// Must be less than 'session_lifetime' +$rcmail_config['min_keep_alive'] = 60; + /***** these settings can be overwritten by user's preferences *****/ +// skin name: folder from skins/ +$rcmail_config['skin'] = 'default'; + // show up to X items in list view $rcmail_config['pagesize'] = 40; // use this timezone to display date/time -$rcmail_config['timezone'] = intval(date('O'))/100 - date('I'); +$rcmail_config['timezone'] = 'auto'; // is daylight saving On? $rcmail_config['dst_active'] = (bool)date('I'); @@ -312,6 +356,12 @@ $rcmail_config['dst_active'] = (bool)date('I'); // prefer displaying HTML messages $rcmail_config['prefer_html'] = TRUE; +// display remote inline images +// 0 - Never, always ask +// 1 - Ask if sender is not in address book +// 2 - Always show inline images +$rcmail_config['show_images'] = 0; + // compose html formatted messages by default $rcmail_config['htmleditor'] = FALSE; @@ -324,26 +374,43 @@ $rcmail_config['draft_autosave'] = 300; // default setting if preview pane is enabled $rcmail_config['preview_pane'] = FALSE; +// focus new window if new message arrives +$rcmail_config['focus_on_new_message'] = true; + // Clear Trash on logout $rcmail_config['logout_purge'] = FALSE; // Compact INBOX on logout $rcmail_config['logout_expunge'] = FALSE; -/** - * 'Delete always' - * This setting reflects if mail should be always marked as deleted, - * even if moving to "Trash" fails. This is necessary in some setups - * because a) people may not have a Trash folder or b) they are over - * quota (and Trash is included in the quota). - * - * This is a failover setting for iil_C_Move when a message is moved - * to the Trash, and not the same as "delete_right_away". - */ -$rcmail_config['delete_always'] = false; +// Display attached images below the message body +$rcmail_config['inline_images'] = TRUE; -// Log successful logins -$rcmail_config['log_logins'] = false; +// Encoding of long/non-ascii attachment names: +// 0 - Full RFC 2231 compatible +// 1 - RFC 2047 for 'name' and RFC 2231 for 'filename' parameter (Thunderbird's default) +// 2 - Full 2047 compatible +$rcmail_config['mime_param_folding'] = 0; + +// Set TRUE if deleted messages should not be displayed +// This will make the application run slower +$rcmail_config['skip_deleted'] = FALSE; + +// Set true to Mark deleted messages as read as well as deleted +// False means that a message's read status is not affected by marking it as deleted +$rcmail_config['read_when_deleted'] = TRUE; + +// When a Trash folder is not present and a message is deleted, flag +// the message for deletion rather than deleting it immediately. Setting this to +// false causes deleted messages to be permanantly removed if there is no Trash folder +$rcmail_config['flag_for_deletion'] = FALSE; + +// Default interval for keep-alive/check-recent requests (in seconds) +// Must be greater than or equal to 'min_keep_alive' and less than 'session_lifetime' +$rcmail_config['keep_alive'] = 60; + +// If true all folders will be checked for recent messages +$rcmail_config['check_all_folders'] = FALSE; // end of config file ?> diff --git a/index.php b/index.php index f40f701..5d19369 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ /* +-------------------------------------------------------------------------+ | RoundCube Webmail IMAP Client | - | Version 0.2-alpha | + | Version 0.2-stable | | | | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland | | | @@ -23,15 +23,18 @@ | Author: Thomas Bruederli | +-------------------------------------------------------------------------+ - $Id: index.php 1499 2008-06-09 20:57:53Z thomasb $ + $Id: index.php 2201 2008-12-30 14:33:28Z thomasb $ */ // include environment require_once 'program/include/iniset.php'; -// define global vars -$OUTPUT_TYPE = 'html'; +// init application and start session with requested task +$RCMAIL = rcmail::get_instance(); + +// init output class +$OUTPUT = !empty($_REQUEST['_remote']) ? $RCMAIL->init_json() : $RCMAIL->load_gui(!empty($_REQUEST['_framed'])); // set output buffering if ($RCMAIL->action != 'get' && $RCMAIL->action != 'viewsource') { @@ -46,13 +49,13 @@ if ($RCMAIL->action != 'get' && $RCMAIL->action != 'viewsource') { } } - -// init application and start session with requested task -$RCMAIL = rcmail::get_instance(); - -// init output class -$OUTPUT = (!empty($_GET['_remote']) || !empty($_POST['_remote'])) ? $RCMAIL->init_json() : $RCMAIL->load_gui((!empty($_GET['_framed']) || !empty($_POST['_framed']))); - +// check if config files had errors +if ($err_str = $RCMAIL->config->get_error()) { + raise_error(array( + 'code' => 601, + 'type' => 'php', + 'message' => $err_str), false, true); +} // check DB connections and exit on failure if ($err_str = $DB->is_error()) { @@ -62,7 +65,6 @@ if ($err_str = $DB->is_error()) { 'message' => $err_str), FALSE, TRUE); } - // error steps if ($RCMAIL->action=='error' && !empty($_GET['_code'])) { raise_error(array('code' => hexdec($_GET['_code'])), FALSE, TRUE); @@ -76,29 +78,29 @@ if ($RCMAIL->action=='login' && $RCMAIL->task=='mail') { if (empty($_COOKIE)) { $OUTPUT->show_message("cookiesdisabled", 'warning'); } - else if ($_SESSION['temp'] && !empty($_POST['_user']) && isset($_POST['_pass']) && + else if ($_SESSION['temp'] && !empty($_POST['_user']) && !empty($_POST['_pass']) && $RCMAIL->login(trim(get_input_value('_user', RCUBE_INPUT_POST), ' '), get_input_value('_pass', RCUBE_INPUT_POST, true, 'ISO-8859-1'), $host)) { // create new session ID unset($_SESSION['temp']); - sess_regenerate_id(); + rcube_sess_regenerate_id(); // send auth cookie if necessary $RCMAIL->authenticate_session(); // log successful login - if ($RCMAIL->config->get('log_logins') && $RCMAIL->config->get('debug_level') & 1) - console(sprintf('Successful login for %s (id %d) from %s', - trim(get_input_value('_user', RCUBE_INPUT_POST), ' '), - $_SESSION['user_id'], - $_SERVER['REMOTE_ADDR'])); + if ($RCMAIL->config->get('log_logins')) { + write_log('userlogins', sprintf('Successful login for %s (id %d) from %s', + $RCMAIL->user->get_username(), + $RCMAIL->user->ID, + $_SERVER['REMOTE_ADDR'])); + } // send redirect - header("Location: {$RCMAIL->comm_path}"); - exit; + $OUTPUT->redirect(); } else { - $OUTPUT->show_message($IMAP->error_code == -1 ? 'imaperror' : 'loginfailed', 'warning'); + $OUTPUT->show_message($IMAP->error_code < -1 ? 'imaperror' : 'loginfailed', 'warning'); $RCMAIL->kill_session(); } } @@ -119,26 +121,9 @@ else if ($RCMAIL->action != 'login' && $_SESSION['user_id'] && $RCMAIL->action ! } -// log in to imap server -if (!empty($RCMAIL->user->ID) && $RCMAIL->task == 'mail') { - if (!$RCMAIL->imap_connect()) { - $RCMAIL->kill_session(); - } -} - - -// not logged in -> set task to 'login -if (empty($RCMAIL->user->ID)) { - if ($OUTPUT->ajax_call) - $OUTPUT->remote_response("setTimeout(\"location.href='\"+this.env.comm_path+\"'\", 2000);"); - - $RCMAIL->set_task('login'); -} - - // check client X-header to verify request origin if ($OUTPUT->ajax_call) { - if (empty($CONFIG['devel_mode']) && !rc_request_header('X-RoundCube-Referer')) { + if (!$RCMAIL->config->get('devel_mode') && !rc_request_header('X-RoundCube-Referer')) { header('HTTP/1.1 404 Not Found'); die("Invalid Request"); } @@ -147,8 +132,12 @@ if ($OUTPUT->ajax_call) { // not logged in -> show login page if (empty($RCMAIL->user->ID)) { + + if ($OUTPUT->ajax_call) + $OUTPUT->redirect(array(), 2000); + // check if installer is still active - if ($CONFIG['enable_installer'] && is_readable('./installer/index.php')) { + if ($RCMAIL->config->get('enable_installer') && is_readable('./installer/index.php')) { $OUTPUT->add_footer(html::div(array('style' => "background:#ef9398; border:2px solid #dc5757; padding:0.5em; margin:2em auto; width:50em"), html::tag('h2', array('style' => "margin-top:0.2em"), "Installer script is still accessible") . html::p(null, "The install script of your RoundCube installation is still stored in its default location!") . @@ -160,135 +149,74 @@ if (empty($RCMAIL->user->ID)) { } $OUTPUT->set_env('task', 'login'); - $OUTPUT->task = 'login'; $OUTPUT->send('login'); - exit; } // handle keep-alive signal -if ($RCMAIL->action=='keep-alive') { +if ($RCMAIL->action == 'keep-alive') { $OUTPUT->reset(); - $OUTPUT->send(''); - exit; + $OUTPUT->send(); } - -// include task specific files -if ($RCMAIL->task=='mail') { - include_once('program/steps/mail/func.inc'); - - if ($RCMAIL->action=='show' || $RCMAIL->action=='preview' || $RCMAIL->action=='print') - include('program/steps/mail/show.inc'); - - if ($RCMAIL->action=='get') - include('program/steps/mail/get.inc'); - - if ($RCMAIL->action=='moveto' || $RCMAIL->action=='delete') - include('program/steps/mail/move_del.inc'); - - if ($RCMAIL->action=='mark') - include('program/steps/mail/mark.inc'); - - if ($RCMAIL->action=='viewsource') - include('program/steps/mail/viewsource.inc'); - - if ($RCMAIL->action=='sendmdn') - include('program/steps/mail/sendmdn.inc'); - - if ($RCMAIL->action=='send') - include('program/steps/mail/sendmail.inc'); - - if ($RCMAIL->action=='upload') - include('program/steps/mail/upload.inc'); - - if ($RCMAIL->action=='compose' || $RCMAIL->action=='remove-attachment' || $RCMAIL->action=='display-attachment') - include('program/steps/mail/compose.inc'); - - if ($RCMAIL->action=='addcontact') - include('program/steps/mail/addcontact.inc'); - - if ($RCMAIL->action=='expunge' || $RCMAIL->action=='purge') - include('program/steps/mail/folders.inc'); - - if ($RCMAIL->action=='check-recent') - include('program/steps/mail/check_recent.inc'); - - if ($RCMAIL->action=='getunread') - include('program/steps/mail/getunread.inc'); - - if ($RCMAIL->action=='list' && isset($_REQUEST['_remote'])) - include('program/steps/mail/list.inc'); - - if ($RCMAIL->action=='search') - include('program/steps/mail/search.inc'); - - if ($RCMAIL->action=='spell') - include('program/steps/mail/spell.inc'); - - if ($RCMAIL->action=='rss') - include('program/steps/mail/rss.inc'); - - // make sure the message count is refreshed - $IMAP->messagecount($_SESSION['mbox'], 'ALL', true); +// save preference value +else if ($RCMAIL->action == 'save-pref') { + $RCMAIL->user->save_prefs(array(get_input_value('_name', RCUBE_INPUT_POST) => get_input_value('_value', RCUBE_INPUT_POST))); + $OUTPUT->reset(); + $OUTPUT->send(); } -// include task specific files -if ($RCMAIL->task=='addressbook') { - include_once('program/steps/addressbook/func.inc'); - - if ($RCMAIL->action=='save') - include('program/steps/addressbook/save.inc'); +// map task/action to a certain include file +$action_map = array( + 'mail' => array( + 'preview' => 'show.inc', + 'print' => 'show.inc', + 'moveto' => 'move_del.inc', + 'delete' => 'move_del.inc', + 'send' => 'sendmail.inc', + 'expunge' => 'folders.inc', + 'purge' => 'folders.inc', + 'remove-attachment' => 'attachments.inc', + 'display-attachment' => 'attachments.inc', + 'upload' => 'attachments.inc', + ), - if ($RCMAIL->action=='edit' || $RCMAIL->action=='add') - include('program/steps/addressbook/edit.inc'); - - if ($RCMAIL->action=='delete') - include('program/steps/addressbook/delete.inc'); - - if ($RCMAIL->action=='show') - include('program/steps/addressbook/show.inc'); - - if ($RCMAIL->action=='list' && $_REQUEST['_remote']) - include('program/steps/addressbook/list.inc'); - - if ($RCMAIL->action=='search') - include('program/steps/addressbook/search.inc'); - - if ($RCMAIL->action=='copy') - include('program/steps/addressbook/copy.inc'); - - if ($RCMAIL->action=='mailto') - include('program/steps/addressbook/mailto.inc'); -} - - -// include task specific files -if ($RCMAIL->task=='settings') { - include_once('program/steps/settings/func.inc'); - - if ($RCMAIL->action=='save-identity') - include('program/steps/settings/save_identity.inc'); - - if ($RCMAIL->action=='add-identity' || $RCMAIL->action=='edit-identity') - include('program/steps/settings/edit_identity.inc'); - - if ($RCMAIL->action=='delete-identity') - include('program/steps/settings/delete_identity.inc'); + 'addressbook' => array( + 'add' => 'edit.inc', + ), - if ($RCMAIL->action=='identities') - include('program/steps/settings/identities.inc'); - - if ($RCMAIL->action=='save-prefs') - include('program/steps/settings/save_prefs.inc'); - - if ($RCMAIL->action=='folders' || $RCMAIL->action=='subscribe' || $RCMAIL->action=='unsubscribe' || - $RCMAIL->action=='create-folder' || $RCMAIL->action=='rename-folder' || $RCMAIL->action=='delete-folder') - include('program/steps/settings/manage_folders.inc'); + 'settings' => array( + 'folders' => 'manage_folders.inc', + 'create-folder' => 'manage_folders.inc', + 'rename-folder' => 'manage_folders.inc', + 'delete-folder' => 'manage_folders.inc', + 'subscribe' => 'manage_folders.inc', + 'unsubscribe' => 'manage_folders.inc', + 'add-identity' => 'edit_identity.inc', + ) +); + +// include task specific functions +include_once 'program/steps/'.$RCMAIL->task.'/func.inc'; + +// allow 5 "redirects" to another action +$redirects = 0; $incstep = null; +while ($redirects < 5) { + $stepfile = !empty($action_map[$RCMAIL->task][$RCMAIL->action]) ? + $action_map[$RCMAIL->task][$RCMAIL->action] : strtr($RCMAIL->action, '-', '_') . '.inc'; + + // try to include the step file + if (is_file(($incfile = 'program/steps/'.$RCMAIL->task.'/'.$stepfile))) { + include($incfile); + $redirects++; + } + else { + break; + } } -// parse main template +// parse main template (default) $OUTPUT->send($RCMAIL->task); diff --git a/installer/._config.php b/installer/._config.php deleted file mode 100644 index 4ee59dc..0000000 Binary files a/installer/._config.php and /dev/null differ diff --git a/installer/check.php b/installer/check.php index 3b64af3..4c34dd6 100644 --- a/installer/check.php +++ b/installer/check.php @@ -1,14 +1,13 @@
'pcre', 'Session' => 'session', - 'DOM XML' => 'dom'); +$required_php_exts = array('PCRE' => 'pcre', 'DOM' => 'dom', 'Session' => 'session'); $optional_php_exts = array('FileInfo' => 'fileinfo', 'Libiconv' => 'iconv', 'Multibyte' => 'mbstring', 'OpenSSL' => 'openssl', 'Mcrypt' => 'mcrypt', 'GD' => 'gd'); -$required_libs = array('PEAR' => 'PEAR.php', 'DB' => 'DB.php', 'MDB2' => 'MDB2.php', +$required_libs = array('PEAR' => 'PEAR.php', 'MDB2' => 'MDB2.php', 'Net_SMTP' => 'Net/SMTP.php', 'Mail_mime' => 'Mail/mime.php', 'iilConnection' => 'lib/imap.inc'); @@ -16,7 +15,7 @@ $supported_dbs = array('MySQL' => 'mysql', 'MySQLi' => 'mysqli', 'PostgreSQL' => 'pgsql', 'SQLite (v2)' => 'sqlite'); $ini_checks = array('file_uploads' => 1, 'session.auto_start' => 0, - 'magic_quotes_gpc' => 0, 'magic_quotes_sybase' => 0); + 'zend.ze1_compatibility_mode' => 0, 'mbstring.func_overload' => 0); $source_urls = array( 'Sockets' => 'http://www.php.net/manual/en/ref.sockets.php', @@ -31,7 +30,8 @@ $source_urls = array( 'PEAR' => 'http://pear.php.net', 'MDB2' => 'http://pear.php.net/package/MDB2', 'Net_SMTP' => 'http://pear.php.net/package/Net_SMTP', - 'Mail_mime' => 'http://pear.php.net/package/Mail_mime' + 'Mail_mime' => 'http://pear.php.net/package/Mail_mime', + 'DOM' => 'http://www.php.net/manual/en/intro.dom.php' ); echo ''; @@ -115,9 +115,6 @@ foreach ($required_libs as $classname => $file) { if (class_exists($classname)) { $RCI->pass($classname); } - else if ($classname == 'DB' || ($classname == 'MDB2' && class_exists('DB'))) { - $RCI->na($classname, 'Use ' . ($classname == 'DB' ? 'MDB2' : 'DB') . ' instead'); - } else { $RCI->fail($classname, "Failed to load $file", $source_urls[$classname]); } diff --git a/installer/config.php b/installer/config.php index 04683b9..fef222d 100644 --- a/installer/config.php +++ b/installer/config.php @@ -6,7 +6,7 @@ $RCI->load_defaults(); // register these boolean fields -$RCI->config_props = array( +$RCI->bool_config_props = array( 'ip_check' => 1, 'enable_caching' => 1, 'enable_spellcheck' => 1, @@ -15,6 +15,7 @@ $RCI->config_props = array( 'prefer_html' => 1, 'preview_pane' => 1, 'htmleditor' => 1, + 'debug_level' => 1, ); // allow the current user to get to the next step @@ -23,16 +24,16 @@ $_SESSION['allowinstaller'] = true; if (!empty($_POST['submit'])) { echo '

Copy or download the following configurations and save them in two files'; - echo ' (names above the text box) within the config/ directory of your RoundCube installation.
'; + echo ' (names above the text box) within the '.RCMAIL_CONFIG_DIR.' directory of your RoundCube installation.
'; echo ' Make sure that there are no characters outside the <?php ?> brackets when saving the files.

'; $textbox = new html_textarea(array('rows' => 16, 'cols' => 60, 'class' => "configfile")); echo '
main.inc.php (download)
'; - echo $textbox->show($RCI->create_config('main')); + echo $textbox->show(($_SESSION['main.inc.php'] = $RCI->create_config('main'))); echo '
db.inc.php (download)
'; - echo $textbox->show($RCI->create_config('db')); + echo $textbox->show($_SESSION['db.inc.php'] = $RCI->create_config('db')); echo '

Of course there are more options to configure. Have a look at the config files or visit Howto_Config to find out.

'; @@ -47,25 +48,6 @@ if (!empty($_POST['submit'])) {
General configuration
-
product_name
@@ -78,17 +60,6 @@ echo $input_prodname->show($RCI->getprop('product_name'));
The name of your service (used to compose page titles)
-
skin_path
-
- '_skin_path', 'size' => 30, 'id' => "cfgskinpath")); -echo $input_skinpath->show($RCI->getprop('skin_path')); - -?> -
Relative path to the skin folder
-
-
temp_dir
'_temp_dir', 'size' => 30, echo $input_tempdir->show($RCI->getprop('temp_dir')); ?> -
Use this folder to store temp files (must be writebale for webserver)
+
Use this folder to store temp files (must be writeable for webserver)
-
log_dir
-
- '_log_dir', 'size' => 30, 'id' => "cfglogdir")); -echo $input_logdir->show($RCI->getprop('log_dir')); - -?> -
Use this folder to store log files (must be writebale for webserver)
-
ip_check
@@ -161,21 +122,109 @@ echo $check_caching->show(intval($RCI->getprop('enable_spellcheck')), array('val

It is based on GoogieSpell what implies that the message content will be sent to Google in order to check the spelling.

-
mdn_requests
+
identities_level
'_mdn_requests', 'id' => "cfgmdnreq")); -$select_mdnreq->add(array('ask the user', 'send automatically', 'ignore'), array(0, 1, 2)); -echo $select_mdnreq->show(intval($RCI->getprop('mdn_requests'))); +$input_ilevel = new html_select(array('name' => '_identities_level', 'id' => "cfgidentitieslevel")); +$input_ilevel->add('many identities with possibility to edit all params', 0); +$input_ilevel->add('many identities with possibility to edit all params but not email address', 1); +$input_ilevel->add('one identity with possibility to edit all params', 2); +$input_ilevel->add('one identity with possibility to edit all params but not email address', 3); +echo $input_ilevel->show($RCI->getprop('identities_level'), 0); ?> -
Behavior if a received message requests a message delivery notification (read receipt)
+
Level of identities access
+

Defines what users can do with their identities.

+
+Logging & Debugging +
+ +
debug_level
+
+getprop('debug_level'); +$check_debug = new html_checkbox(array('name' => '_debug_level[]')); +echo $check_debug->show(($value & 1) ? 1 : 0 , array('value' => 1, 'id' => 'cfgdebug1')); +echo '
'; + +echo $check_debug->show(($value & 4) ? 4 : 0, array('value' => 4, 'id' => 'cfgdebug4')); +echo '
'; + +echo $check_debug->show(($value & 8) ? 8 : 0, array('value' => 8, 'id' => 'cfgdebug8')); +echo '
'; + +?> +
+ +
log_driver
+
+ '_log_driver', 'id' => "cfglogdriver")); +$select_log_driver->add(array('file', 'syslog'), array('file', 'syslog')); +echo $select_log_driver->show($RCI->getprop('log_driver', 'file')); + +?> +
How to do logging? 'file' - write to files in the log directory, 'syslog' - use the syslog facility.
+
+ +
log_dir
+
+ '_log_dir', 'size' => 30, 'id' => "cfglogdir")); +echo $input_logdir->show($RCI->getprop('log_dir')); + +?> +
Use this folder to store log files (must be writeable for webserver). Note that this only applies if you are using the 'file' log_driver.
+
+ +
syslog_id
+
+ '_syslog_id', 'size' => 30, 'id' => "cfgsyslogid")); +echo $input_syslogid->show($RCI->getprop('syslog_id', 'roundcube')); + +?> +
What ID to use when logging with syslog. Note that this only applies if you are using the 'syslog' log_driver.
+
+ +
syslog_facility
+
+ '_syslog_facility', 'id' => "cfgsyslogfacility")); +$input_syslogfacility->add('user-level messages', LOG_USER); +$input_syslogfacility->add('mail subsystem', LOG_MAIL); +$input_syslogfacility->add('local level 0', LOG_LOCAL0); +$input_syslogfacility->add('local level 1', LOG_LOCAL1); +$input_syslogfacility->add('local level 2', LOG_LOCAL2); +$input_syslogfacility->add('local level 3', LOG_LOCAL3); +$input_syslogfacility->add('local level 4', LOG_LOCAL4); +$input_syslogfacility->add('local level 5', LOG_LOCAL5); +$input_syslogfacility->add('local level 6', LOG_LOCAL6); +$input_syslogfacility->add('local level 7', LOG_LOCAL7); +echo $input_syslogfacility->show($RCI->getprop('syslog_facility'), LOG_USER); + +?> +
What ID to use when logging with syslog. Note that this only applies if you are using the 'syslog' log_driver.
+
+ + + + +
+
+ +
Database setup
@@ -216,28 +265,6 @@ echo '
'; ?> - -
db_backend
-
- '_db_backend', 'id' => "cfgdbba")); - -if (class_exists('DB')) - $select_dbba->add('DB', 'db'); -if (class_exists('MDB2')) - $select_dbba->add('MDB2', 'mdb2'); - -echo $select_dbba->show($RCI->getprop('db_backend')); - -?> -
PEAR Database backend to use
-
-
@@ -321,7 +348,7 @@ $text_sentmbox = new html_inputfield(array('name' => '_sent_mbox', 'size' => 20, echo $text_sentmbox->show($RCI->getprop('sent_mbox')); ?> -
Store sent messages is this folder
+
Store sent messages in this folder

Leave blank if sent messages should not be stored

@@ -347,9 +374,21 @@ $text_draftsmbox = new html_inputfield(array('name' => '_drafts_mbox', 'size' => echo $text_draftsmbox->show($RCI->getprop('drafts_mbox')); ?> -
Store draft messages is this folder
+
Store draft messages in this folder
+ +

Leave blank if they should not be stored

+
junk_mbox
+
+ '_junk_mbox', 'size' => 20, 'id' => "cfgjunkmbox")); +echo $text_junkmbox->show($RCI->getprop('junk_mbox')); + +?> +
Store spam messages in this folder
+
@@ -423,7 +462,7 @@ $check_smtplog = new html_checkbox(array('name' => '_smtp_log', 'id' => "cfgsmtp echo $check_smtplog->show(intval($RCI->getprop('smtp_log')), array('value' => 1)); ?> -
+
@@ -434,16 +473,27 @@ echo $check_smtplog->show(intval($RCI->getprop('smtp_log')), array('value' => 1) Display settings & user prefs
-
locale_string
+
language
'_locale_string', 'size' => 6, 'id' => "cfglocale")); -echo $input_locale->show($RCI->getprop('locale_string')); +$input_locale = new html_inputfield(array('name' => '_language', 'size' => 6, 'id' => "cfglocale")); +echo $input_locale->show($RCI->getprop('language')); ?> -
The default locale setting. This also defines the language of the login screen.
-

Enter a RFC1766 formatted locale name. Examples: en_US, de, de_CH, fr, pt_BR

+
The default locale setting. This also defines the language of the login screen.
Leave it empty to auto-detect the user agent language.
+

Enter a RFC1766 formatted language name. Examples: en_US, de_DE, de_CH, fr_FR, pt_BR

+
+ +
skin *
+
+ '_skin', 'size' => 30, 'id' => "cfgskin")); +echo $input_skin->show($RCI->getprop('skin')); + +?> +
Name of interface skin (folder in /skins)
pagesize *
@@ -505,6 +555,33 @@ echo $select_autosave->show(intval($RCI->getprop('draft_autosave'))); ?> +
mdn_requests *
+
+ '_mdn_requests', 'id' => "cfgmdnreq")); +$select_mdnreq->add(array('ask the user', 'send automatically', 'ignore'), array(0, 1, 2)); +echo $select_mdnreq->show(intval($RCI->getprop('mdn_requests'))); + +?> +
Behavior if a received message requests a message delivery notification (read receipt)
+
+ +
mime_param_folding *
+
+ '_mime_param_folding', 'id' => "cfgmimeparamfolding")); +$select_param_folding->add('Full RFC 2231 (Roundcube, Thunderbird)', '0'); +$select_param_folding->add('RFC 2047/2231 (MS Outlook, OE)', '1'); +$select_param_folding->add('Full RFC 2047 (deprecated)', '2'); + +echo $select_param_folding->show(intval($RCI->getprop('mime_param_folding'))); + +?> +
How to encode attachment long/non-ascii names
+
+

*  These settings are defaults for the user preferences

diff --git a/installer/images/error.png b/installer/images/error.png new file mode 100755 index 0000000..628cf2d Binary files /dev/null and b/installer/images/error.png differ diff --git a/installer/index.php b/installer/index.php index 4bd37f3..f7a5cea 100644 --- a/installer/index.php +++ b/installer/index.php @@ -1,8 +1,11 @@ load_config(); + +if (isset($_GET['_getfile']) && in_array($_GET['_getfile'], array('main', 'db'))) { + $filename = $_GET['_getfile'] . '.inc.php'; + if (!empty($_SESSION[$filename])) { + header('Content-type: text/plain'); + header('Content-Disposition: attachment; filename="'.$filename.'"'); + echo $_SESSION[$filename]; + exit; + } + else { + header('HTTP/1.0 404 Not found'); + die("The requested configuration was not found. Please run the installer from the beginning."); + } } + +if ($RCI->configured && ($RCI->getprop('enable_installer') || $_SESSION['allowinstaller']) && + isset($_GET['_mergeconfig']) && in_array($_GET['_mergeconfig'], array('main', 'db'))) { + $filename = $_GET['_mergeconfig'] . '.inc.php'; + + header('Content-type: text/plain'); + header('Content-Disposition: attachment; filename="'.$filename.'"'); + + $RCI->merge_config(); + echo $RCI->create_config($_GET['_mergeconfig'], true); + exit; +} + +// go to 'test' step if we have a local configuration +if ($RCI->configured && empty($_REQUEST['_step'])) { + header("Location: ./?_step=3"); + exit; +} + ?> RoundCube Webmail Installer - + + @@ -52,14 +80,11 @@ function __autoload($classname) load_config(); - // exit if installation is complete if ($RCI->configured && !$RCI->getprop('enable_installer') && !$_SESSION['allowinstaller']) { // header("HTTP/1.0 404 Not Found"); echo '

The installer is disabled!

'; - echo '

To enable it again, set $rcmail_config[\'enable_installer\'] = true; in config/main.inc.php

'; + echo '

To enable it again, set $rcmail_config[\'enable_installer\'] = true; in RCMAIL_CONFIG_DIR/main.inc.php

'; echo ''; exit; } diff --git a/installer/rcube_install.php b/installer/rcube_install.php index 0240467..96134d2 100644 --- a/installer/rcube_install.php +++ b/installer/rcube_install.php @@ -30,7 +30,25 @@ class rcube_install var $configured = false; var $last_error = null; var $email_pattern = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9])'; - var $config_props = array(); + var $bool_config_props = array(); + + var $obsolete_config = array('db_backend'); + var $replaced_config = array( + 'skin_path' => 'skin', + 'locale_string' => 'language', + 'multiple_identities' => 'identities_level', + 'addrbook_show_images' => 'show_images', + ); + + // these config options are optional or can be set to null + var $optional_config = array( + 'log_driver', 'syslog_id', 'syslog_facility', 'imap_auth_type', + 'smtp_helo_host', 'smtp_auth_type', 'sendmail_delay', 'double_auth', + 'language', 'mail_header_delimiter', 'create_default_folders', + 'quota_zero_as_unlimited', 'spellcheck_uri', 'spellcheck_languages', + 'http_received_header', 'session_domain', 'mime_magic', 'log_logins', + 'enable_installer', 'skin_include_php', 'imap_root', 'imap_delimiter', + 'virtuser_file', 'virtuser_query', 'dont_override'); /** * Constructor @@ -79,12 +97,12 @@ class rcube_install */ function _load_config($suffix) { - @include '../config/main.inc' . $suffix; + @include RCMAIL_CONFIG_DIR . '/main.inc' . $suffix; if (is_array($rcmail_config)) { $this->config += $rcmail_config; } - @include '../config/db.inc'. $suffix; + @include RCMAIL_CONFIG_DIR . '/db.inc'. $suffix; if (is_array($rcmail_config)) { $this->config += $rcmail_config; } @@ -100,7 +118,7 @@ class rcube_install */ function getprop($name, $default = '') { - $value = $this->is_post && (isset($_POST["_$name"]) || $this->config_props[$name]) ? $_POST["_$name"] : $this->config[$name]; + $value = $this->config[$name]; if ($name == 'des_key' && !$this->configured && !isset($_REQUEST["_$name"])) $value = rcube_install::random_key(24); @@ -116,30 +134,30 @@ class rcube_install * @param string Which config file (either 'main' or 'db') * @return string The complete config file content */ - function create_config($which) + function create_config($which, $force = false) { - $out = file_get_contents("../config/{$which}.inc.php.dist"); + $out = file_get_contents(RCMAIL_CONFIG_DIR . "/{$which}.inc.php.dist"); if (!$out) return '[Warning: could not read the template file]'; - + foreach ($this->config as $prop => $default) { - $value = (isset($_POST["_$prop"]) || $this->config_props[$prop]) ? $_POST["_$prop"] : $default; + $value = (isset($_POST["_$prop"]) || $this->bool_config_props[$prop]) ? $_POST["_$prop"] : $default; // convert some form data - if ($prop == 'debug_level' && is_array($value)) { + if ($prop == 'debug_level') { $val = 0; - foreach ($value as $i => $dbgval) - $val += intval($dbgval); + if (is_array($value)) + foreach ($value as $dbgval) + $val += intval($dbgval); $value = $val; } - else if ($prop == 'db_dsnw' && !empty($_POST['_dbtype'])) { + else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) { if ($_POST['_dbtype'] == 'sqlite') $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']); else $value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'], - rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), - $_POST['_dbhost'], $_POST['_dbname']); + rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']); } else if ($prop == 'smtp_auth_type' && $value == '0') { $value = ''; @@ -166,18 +184,216 @@ class rcube_install } // skip this property - if ($value == $default) + if (!$force && ($value == $default)) continue; - + + // save change + $this->config[$prop] = $value; + // replace the matching line in config file $out = preg_replace( '/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie', - "'\\1 = ' . var_export(\$value, true) . ';'", + "'\\1 = ' . rcube_install::_dump_var(\$value) . ';'", $out); } - + return trim($out); } + + + /** + * Check the current configuration for missing properties + * and deprecated or obsolete settings + * + * @return array List with problems detected + */ + function check_config() + { + $this->config = array(); + $this->load_defaults(); + $defaults = $this->config; + + $this->load_config(); + if (!$this->configured) + return null; + + $out = $seen = array(); + $optional = array_flip($this->optional_config); + + // iterate over the current configuration + foreach ($this->config as $prop => $value) { + if ($replacement = $this->replaced_config[$prop]) { + $out['replaced'][] = array('prop' => $prop, 'replacement' => $replacement); + $seen[$replacement] = true; + } + else if (!$seen[$prop] && in_array($prop, $this->obsolete_config)) { + $out['obsolete'][] = array('prop' => $prop); + $seen[$prop] = true; + } + } + + // iterate over default config + foreach ($defaults as $prop => $value) { + if (!$seen[$prop] && !isset($this->config[$prop]) && !isset($optional[$prop])) + $out['missing'][] = array('prop' => $prop); + } + + // check config dependencies and contradictions + if ($this->config['enable_spellcheck'] && $this->config['spellcheck_engine'] == 'pspell') { + if (!extension_loaded('pspell')) { + $out['dependencies'][] = array('prop' => 'spellcheck_engine', + 'explain' => 'This requires the pspell extension which could not be loaded.'); + } + if (empty($this->config['spellcheck_languages'])) { + $out['dependencies'][] = array('prop' => 'spellcheck_languages', + 'explain' => 'You should specify the list of languages supported by your local pspell installation.'); + } + } + + if ($this->config['log_driver'] == 'syslog') { + if (!function_exists('openlog')) { + $out['dependencies'][] = array('prop' => 'log_driver', + 'explain' => 'This requires the sylog extension which could not be loaded.'); + } + if (empty($this->config['syslog_id'])) { + $out['dependencies'][] = array('prop' => 'syslog_id', + 'explain' => 'Using syslog for logging requires a syslog ID to be configured'); + } + } + + // check ldap_public sources having global_search enabled + if (is_array($this->config['ldap_public']) && !is_array($this->config['autocomplete_addressbooks'])) { + foreach ($this->config['ldap_public'] as $ldap_public) { + if ($ldap_public['global_search']) { + $out['replaced'][] = array('prop' => 'ldap_public::global_search', 'replacement' => 'autocomplete_addressbooks'); + break; + } + } + } + + return $out; + } + + + /** + * Merge the current configuration with the defaults + * and copy replaced values to the new options. + */ + function merge_config() + { + $current = $this->config; + $this->config = array(); + $this->load_defaults(); + + foreach ($this->replaced_config as $prop => $replacement) + if (isset($current[$prop])) { + if ($prop == 'skin_path') + $this->config[$replacement] = preg_replace('#skins/(\w+)/?$#', '\\1', $current[$prop]); + else if ($prop == 'multiple_identities') + $this->config[$replacement] = $current[$prop] ? 2 : 0; + else + $this->config[$replacement] = $current[$prop]; + + unset($current[$prop]); + } + + foreach ($this->obsolete_config as $prop) { + unset($current[$prop]); + } + + // add all ldap_public sources having global_search enabled to autocomplete_addressbooks + if (is_array($current['ldap_public'])) { + foreach ($current['ldap_public'] as $key => $ldap_public) { + if ($ldap_public['global_search']) { + $this->config['autocomplete_addressbooks'][] = $key; + unset($current['ldap_public'][$key]['global_search']); + } + } + } + + $this->config = array_merge($this->config, $current); + + foreach ((array)$current['ldap_public'] as $key => $values) { + $this->config['ldap_public'][$key] = $current['ldap_public'][$key]; + } + } + + + /** + * Compare the local database schema with the reference schema + * required for this version of RoundCube + * + * @param boolean True if the schema schould be updated + * @return boolean True if the schema is up-to-date, false if not or an error occured + */ + function db_schema_check($update = false) + { + if (!$this->configured) + return false; + + $options = array( + 'use_transactions' => false, + 'log_line_break' => "\n", + 'idxname_format' => '%s', + 'debug' => false, + 'quote_identifier' => true, + 'force_defaults' => false, + 'portability' => true + ); + + $schema =& MDB2_Schema::factory($this->config['db_dsnw'], $options); + $schema->db->supported['transactions'] = false; + + if (PEAR::isError($schema)) { + $this->raise_error(array('code' => $schema->getCode(), 'message' => $schema->getMessage() . ' ' . $schema->getUserInfo())); + return false; + } + else { + $definition = $schema->getDefinitionFromDatabase(); + $definition['charset'] = 'utf8'; + + if (PEAR::isError($definition)) { + $this->raise_error(array('code' => $definition->getCode(), 'message' => $definition->getMessage() . ' ' . $definition->getUserInfo())); + return false; + } + + // load reference schema + $dsn = MDB2::parseDSN($this->config['db_dsnw']); + $ref_schema = INSTALL_PATH . 'SQL/' . $dsn['phptype'] . '.schema.xml'; + + if (is_file($ref_schema)) { + $reference = $schema->parseDatabaseDefinition($ref_schema, false, array(), $schema->options['fail_on_invalid_names']); + + if (PEAR::isError($reference)) { + $this->raise_error(array('code' => $reference->getCode(), 'message' => $reference->getMessage() . ' ' . $reference->getUserInfo())); + } + else { + $diff = $schema->compareDefinitions($reference, $definition); + + if (empty($diff)) { + return true; + } + else if ($update) { + // update database schema with the diff from the above check + $success = $schema->alterDatabase($reference, $definition, $diff); + + if (PEAR::isError($success)) { + $this->raise_error(array('code' => $success->getCode(), 'message' => $success->getMessage() . ' ' . $success->getUserInfo())); + } + else + return true; + } + echo '
'; var_dump($diff); echo '
'; + return false; + } + } + else + $this->raise_error(array('message' => "Could not find reference schema file ($ref_schema)")); + return false; + } + + return false; + } /** @@ -265,18 +481,46 @@ class rcube_install } - function _clean_array($arr) + static function _clean_array($arr) { $out = array(); - foreach (array_unique($arr) as $i => $val) - if (!empty($val)) - $out[] = $val; + foreach (array_unique($arr) as $k => $val) { + if (!empty($val)) { + if (is_numeric($k)) + $out[] = $val; + else + $out[$k] = $val; + } + } return $out; } + static function _dump_var($var) { + if (is_array($var)) { + if (empty($var)) { + return 'array()'; + } + else { // check if all keys are numeric + $isnum = true; + foreach ($var as $key => $value) { + if (!is_numeric($key)) { + $isnum = false; + break; + } + } + + if ($isnum) + return 'array(' . join(', ', array_map(array('rcube_install', '_dump_var'), $var)) . ')'; + } + } + + return var_export($var, true); + } + + /** * Initialize the database with the according schema * @@ -288,16 +532,6 @@ class rcube_install $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql'); $engine = isset($db_map[$DB->db_provider]) ? $db_map[$DB->db_provider] : $DB->db_provider; - // find out db version - if ($engine == 'mysql') { - $DB->query('SELECT VERSION() AS version'); - $sql_arr = $DB->fetch_assoc(); - $version = floatval($sql_arr['version']); - - if ($version >= 4.1) - $engine = 'mysql5'; - } - // read schema file from /SQL/* $fname = "../SQL/$engine.initial.sql"; if ($lines = @file($fname, FILE_SKIP_EMPTY_LINES)) { @@ -357,25 +591,3 @@ class rcube_install } - -/** - * Shortcut function for htmlentities() - * - * @param string String to quote - * @return string The html-encoded string - */ -function Q($string) -{ - return htmlentities($string); -} - - -/** - * Fake rinternal error handler to catch errors - */ -function raise_error($p) -{ - $rci = rcube_install::get_instance(); - $rci->raise_error($p); -} - diff --git a/installer/styles.css b/installer/styles.css index ed64ff0..1acdc9c 100644 --- a/installer/styles.css +++ b/installer/styles.css @@ -131,7 +131,7 @@ textarea.configfile { height: 30em; } -dt.propname { +.propname { font-family: monospace; font-size: 9pt; margin-top: 1em; @@ -219,12 +219,25 @@ a.removelink { border: 2px solid #c2d071; } -.warning { +.suggestion { + padding: 0.6em; + background-color: #ebebeb; + border: 1px solid #999; +} + +p.warning, +div.warning { padding: 1em; background-color: #ef9398; border: 2px solid #dc5757; } +h3.warning { + color: #c00; + background: url('images/error.png') top left no-repeat; + padding-left: 24px; +} + .userconf { color: #00c; font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; diff --git a/installer/test.php b/installer/test.php index bdaf3ee..d66fe34 100644 --- a/installer/test.php +++ b/installer/test.php @@ -3,8 +3,8 @@

Check config files

config)) { $RCI->pass('main.inc.php'); @@ -27,6 +27,64 @@ else if (!$read_db) { $RCI->fail('db.inc.php', 'Unable to read file. Did you create the config files?'); } +if ($RCI->configured && ($messages = $RCI->check_config())) { + + if (is_array($messages['missing'])) { + echo '

Missing config options

'; + echo '

The following config options are not present in the current configuration.
'; + echo 'Please check the default config files and add the missing properties to your local config files.

'; + + echo '
    '; + foreach ($messages['missing'] as $msg) { + echo html::tag('li', null, html::span('propname', $msg['prop']) . ($msg['name'] ? ': ' . $msg['name'] : '')); + } + echo '
'; + } + + if (is_array($messages['replaced'])) { + echo '

Replaced config options

'; + echo '

The following config options have been replaced or renamed. '; + echo 'Please update them accordingly in your config files.

'; + + echo '
    '; + foreach ($messages['replaced'] as $msg) { + echo html::tag('li', null, html::span('propname', $msg['prop']) . + ' was replaced by ' . html::span('propname', $msg['replacement'])); + } + echo '
'; + } + + if (is_array($messages['obsolete'])) { + echo '

Obsolete config options

'; + echo '

You still have some obsolete or inexistent properties set. This isn\'t a problem but should be noticed.

'; + + echo '
    '; + foreach ($messages['obsolete'] as $msg) { + echo html::tag('li', null, html::span('propname', $msg['prop']) . ($msg['name'] ? ': ' . $msg['name'] : '')); + } + echo '
'; + } + + echo '

OK, lazy people can download the updated config files here: '; + echo html::a(array('href' => './?_mergeconfig=main'), 'main.inc.php') . '  '; + echo html::a(array('href' => './?_mergeconfig=db'), 'db.inc.php'); + echo "

"; + + + if (is_array($messages['dependencies'])) { + echo '

Dependency check failed

'; + echo '

Some of your configuration settings require other options to be configured or additional PHP modules to be installed

'; + + echo '
    '; + foreach ($messages['dependencies'] as $msg) { + echo html::tag('li', null, html::span('propname', $msg['prop']) . ': ' . $msg['explain']); + } + echo '
'; + } + + +} + ?>

Check if directories are writable

@@ -35,7 +93,12 @@ else if (!$read_db) { if ($RCI->configured) { $pass = false; - foreach (array($RCI->config['temp_dir'],$RCI->config['log_dir']) as $dir) { + + $dirs[] = $RCI->config['temp_dir']; + if($RCI->config['log_driver'] != 'syslog') + $dirs[] = $RCI->config['log_dir']; + + foreach ($dirs as $dir) { $dirpath = $dir{0} == '/' ? $dir : INSTALL_PATH . $dir; if (is_writable(realpath($dirpath))) { $RCI->pass($dir); @@ -61,14 +124,9 @@ else { $db_working = false; if ($RCI->configured) { - if (!empty($RCI->config['db_backend']) && !empty($RCI->config['db_dsnw'])) { - - echo 'Backend: '; - echo 'PEAR::' . strtoupper($RCI->config['db_backend']) . '
'; + if (!empty($RCI->config['db_dsnw'])) { - $dbclass = 'rcube_' . strtolower($RCI->config['db_backend']); - - $DB = new $dbclass($RCI->config['db_dsnw'], '', false); + $DB = new rcube_mdb2($RCI->config['db_dsnw'], '', false); $DB->db_connect('w'); if (!($db_error_msg = $DB->is_error())) { $RCI->pass('DSN (write)'); @@ -79,8 +137,6 @@ if ($RCI->configured) { $RCI->fail('DSN (write)', $db_error_msg); echo '

Make sure that the configured database exists and that the user has write privileges
'; echo 'DSN: ' . $RCI->config['db_dsnw'] . '

'; - if ($RCI->config['db_backend'] == 'mdb2') - echo '

There are known problems with MDB2 running on PHP 4. Try setting db_backend to \'db\' instead

'; } } else { @@ -105,13 +161,22 @@ if ($db_working) { $db_read = $DB->query("SELECT count(*) FROM {$RCI->config['db_table_users']}"); if (!$db_read) { $RCI->fail('DB Schema', "Database not initialized"); - $db_working = false; echo '

'; + $db_working = false; + } + /* + else if (!$RCI->db_schema_check($update = !empty($_POST['updatedb']))) { + $RCI->fail('DB Schema', "Database schema differs"); + + echo $update ? '

Failed to update the database schema! Please manually execute the SQL statements from the SQL/*.update.sql file on your database

' : + '

'; + $db_working = false; } + */ else { $RCI->pass('DB Schema'); + echo '
'; } - echo '
'; } // more database tests @@ -127,7 +192,7 @@ if ($db_working) { else { $RCI->fail('DB Write', $RCI->get_error()); } - echo '
'; + echo '
'; // check timezone settings $tz_db = 'SELECT ' . $DB->unixtimestamp($DB->now()) . ' AS tz_db'; @@ -239,10 +304,10 @@ if (isset($_POST['sendmail']) && !empty($_POST['_from']) && !empty($_POST['_to'] else { $RCI->fail('SMTP send', 'Invalid sender or recipient'); } + + echo '

'; } -echo '

'; - ?> diff --git a/installer/utils.php b/installer/utils.php new file mode 100644 index 0000000..c1775f2 --- /dev/null +++ b/installer/utils.php @@ -0,0 +1,40 @@ +raise_error($p); +} + + diff --git a/installer/welcome.html b/installer/welcome.html index bfec233..0c6805a 100644 --- a/installer/welcome.html +++ b/installer/welcome.html @@ -22,7 +22,6 @@
  • error_reporting E_ALL & ~E_NOTICE (or lower)
  • file_uploads on (for attachment upload features)
  • session.auto_start needs to be off
  • -
  • magic_quotes_gpc off
  • A MySQL or PostgreSQL database engine or the SQLite extension for PHP
  • diff --git a/program/include/._main.inc b/program/include/._main.inc deleted file mode 100644 index f28074c..0000000 Binary files a/program/include/._main.inc and /dev/null differ diff --git a/program/include/bugs.inc b/program/include/bugs.inc index 6987a20..e5dcf42 100644 --- a/program/include/bugs.inc +++ b/program/include/bugs.inc @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: bugs.inc 1291 2008-04-12 13:54:45Z thomasb $ + $Id: bugs.inc 2025 2008-10-30 09:15:01Z alec $ */ @@ -67,25 +67,31 @@ function raise_error($arg=array(), $log=false, $terminate=false) function log_bug($arg_arr) { global $CONFIG; - $program = $arg_arr['type']=='xpath' ? 'XPath' : strtoupper($arg_arr['type']); + $program = 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", + $post_query = ($_SERVER['REQUEST_METHOD'] == 'POST' ? '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']) : ''); + $log_entry = sprintf("[%s] %s Error: %s%s (%s %s)\n", date("d-M-Y H:i:s O", mktime()), $program, $arg_arr['message'], - $arg_arr['file'], - $arg_arr['line']); - + $arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '', + $_SERVER['REQUEST_METHOD'], + $_SERVER['REQUEST_URI'] . $post_query); + if (empty($CONFIG['log_dir'])) $CONFIG['log_dir'] = INSTALL_PATH.'logs'; // try to open specific log file for writing - if ($fp = @fopen($CONFIG['log_dir'].'/errors', 'a')) + if ($CONFIG['log_driver'] == 'syslog') + { + syslog(LOG_ERR, $log_entry); + } + else if ($fp = @fopen($CONFIG['log_dir'].'/errors', 'a')) { + // log_driver == 'file' is the default, assumed here. fwrite($fp, $log_entry); fclose($fp); } diff --git a/program/include/html.php b/program/include/html.php index d0ab976..032915f 100644 --- a/program/include/html.php +++ b/program/include/html.php @@ -29,11 +29,11 @@ class html { protected $tagname; protected $attrib = array(); - protected $allowed; + protected $allowed = array(); protected $content; public static $common_attrib = array('id','class','style','title','align'); - public static $containers = array('div','span','p','h1','h2','h3','form','textarea'); + public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','tr','th','td','style'); public static $lc_tags = true; /** @@ -55,7 +55,7 @@ class html */ public function show() { - return self::tag($this->tagname, $this->attrib, $this->content, $this->allowed); + return self::tag($this->tagname, $this->attrib, $this->content, array_merge(self::$common_attrib, $this->allowed)); } /****** STATIC METHODS *******/ @@ -98,7 +98,7 @@ class html if (is_string($attr)) { $attr = array('class' => $attr); } - return self::tag('div', $attr, $cont, self::$common_attrib); + return self::tag('div', $attr, $cont, array_merge(self::$common_attrib, array('onclick'))); } /** @@ -145,7 +145,7 @@ class html if (is_string($attr)) { $attr = array('href' => $attr); } - return self::tag('a', $attr, $cont, array_merge(self::$common_attrib, array('href','target','name','onclick','onmouseover','onmouseout'))); + return self::tag('a', $attr, $cont, array_merge(self::$common_attrib, array('href','target','name','onclick','onmouseover','onmouseout','onmousedown','onmouseup'))); } /** @@ -180,6 +180,21 @@ class html return self::tag('label', $attr, $cont, array_merge(self::$common_attrib, array('for'))); } + /** + * Derrived method to create + * + * @param mixed Hash array with tag attributes or string with frame source (src) + * @return string HTML code + * @see html::tag() + */ + public static function iframe($attr = null, $cont = null) + { + if (is_string($attr)) { + $attr = array('src' => $attr); + } + return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib, array('src','name','width','height','border','frameborder'))); + } + /** * Derrived method for line breaks * @@ -248,6 +263,7 @@ class html_inputfield extends html { protected $tagname = 'input'; protected $type = 'text'; + protected $allowed = array('type','name','value','size','tabindex','autocomplete','checked','onchange','onclick','disabled','readonly','spellcheck','results'); public function __construct($attrib = array()) { @@ -415,7 +431,7 @@ class html_checkbox extends html_inputfield class html_textarea extends html { protected $tagname = 'textarea'; - protected $allowed_attrib = array('name','rows','cols','wrap','tabindex'); + protected $allowed = array('name','rows','cols','wrap','tabindex','onchange','disabled','readonly','spellcheck'); /** * Get HTML code for this object @@ -441,10 +457,11 @@ class html_textarea extends html unset($this->attrib['value']); } - if (!empty($value) && !isset($this->attrib['mce_editable'])) { + if (!empty($value) && !ereg('mce_editor', $this->attrib['class'])) { $value = Q($value, 'strict', false); } - return self::tag($this->tagname, $this->attrib, $value, array_merge(self::$common_attrib, $this->allowed_attrib)); + + return self::tag($this->tagname, $this->attrib, $value, array_merge(self::$common_attrib, $this->allowed)); } } @@ -471,6 +488,7 @@ class html_select extends html { protected $tagname = 'select'; protected $options = array(); + protected $allowed = array('name','size','tabindex','autocomplete','multiple','onchange','disabled'); /** * Add a new option to this drop-down @@ -511,7 +529,7 @@ class html_select extends html $attr = array( 'value' => $option['value'], 'selected' => (in_array($option['value'], $select, true) || - in_array($option['text'], $select, true)) ? 1 : null); + in_array($option['text'], $select, true)) ? 1 : null); $this->content .= self::tag('option', $attr, Q($option['text'])); } @@ -570,10 +588,10 @@ class html_table extends html * @param array Cell attributes * @param string Cell content */ - private function add_header($attr, $cont) + public function add_header($attr, $cont) { if (is_string($attr)) - $attr = array('class' => $attr); + $attr = array('class' => $attr); $cell = new stdClass; $cell->attrib = $attr; @@ -586,7 +604,7 @@ class html_table extends html * * @param array Row attributes */ - private function add_row($attr = array()) + public function add_row($attr = array()) { $this->rowindex++; $this->colindex = 0; @@ -595,6 +613,18 @@ class html_table extends html $this->rows[$this->rowindex]->cells = array(); } + /** + * Set current row attrib + * + * @param array Row attributes + */ + public function set_row_attribs($attr = array()) + { + if (is_string($attr)) + $attr = array('class' => $attr); + + $this->rows[$this->rowindex]->attrib = $attr; + } /** * Build HTML output of the table data @@ -602,16 +632,18 @@ class html_table extends html * @param array Table attributes * @return string The final table HTML code */ - public function show($attr = array()) + public function show($attrib = null) { - $this->attrib = array_merge($this->attrib, $attr); + if (is_array($attrib)) + $this->attrib = array_merge($this->attrib, $attrib); + $thead = $tbody = ""; // include if (!empty($this->header)) { $rowcontent = ''; foreach ($this->header as $c => $col) { - $rowcontent .= self::tag('th', $col->attrib, $col->content); + $rowcontent .= self::tag('td', $col->attrib, $col->content); } $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent)); } @@ -623,7 +655,7 @@ class html_table extends html } if ($r < $this->rowindex || count($row->cells)) { - $tbody .= self::tag('tr', $rows->attrib, $rowcontent); + $tbody .= self::tag('tr', $row->attrib, $rowcontent); } } @@ -637,6 +669,16 @@ class html_table extends html unset($this->attrib['cols'], $this->attrib['rowsonly']); return parent::show(); } + + /** + * Count number of rows + * + * @return The number of rows + */ + public function size() + { + return count($this->rows); + } } -?> \ No newline at end of file +?> diff --git a/program/include/iniset.php b/program/include/iniset.php index b718393..78eb270 100755 --- a/program/include/iniset.php +++ b/program/include/iniset.php @@ -22,7 +22,7 @@ // application constants -define('RCMAIL_VERSION', '0.2-alpha'); +define('RCMAIL_VERSION', '0.2-stable'); define('RCMAIL_CHARSET', 'UTF-8'); define('JS_OBJECT_NAME', 'rcmail'); @@ -30,6 +30,8 @@ if (!defined('INSTALL_PATH')) { define('INSTALL_PATH', dirname($_SERVER['SCRIPT_FILENAME']).'/'); } +define('RCMAIL_CONFIG_DIR', INSTALL_PATH . 'config'); + // make sure path_separator is defined if (!defined('PATH_SEPARATOR')) { define('PATH_SEPARATOR', (eregi('win', PHP_OS) ? ';' : ':')); @@ -50,8 +52,8 @@ if (set_include_path($include_path) === false) { ini_set('session.name', 'roundcube_sessid'); ini_set('session.use_cookies', 1); -ini_set('session.gc_maxlifetime', 21600); -ini_set('session.gc_divisor', 500); +ini_set('session.only_use_cookies', 1); +ini_set('session.cookie_secure', ($_SERVER['HTTPS'] && ($_SERVER['HTTPS'] != 'off'))); ini_set('error_reporting', E_ALL&~E_NOTICE); set_magic_quotes_runtime(0); @@ -61,6 +63,11 @@ if (!ini_get('safe_mode')) { set_time_limit(120); } +// set internal encoding for mbstring extension +if(extension_loaded('mbstring')) + mb_internal_encoding(RCMAIL_CHARSET); + + /** * Use PHP5 autoload for dynamic class loading * @@ -69,8 +76,18 @@ if (!ini_get('safe_mode')) { function __autoload($classname) { $filename = preg_replace( - array('/MDB2_(.+)/', '/Mail_(.+)/', '/^html_.+/', '/^utf8$/'), - array('MDB2/\\1', 'Mail/\\1', 'html', 'utf8.class'), + array('/MDB2_(.+)/', + '/Mail_(.+)/', + '/^html_.+/', + '/^utf8$/', + '/html2text/' + ), + array('MDB2/\\1', + 'Mail/\\1', + 'html', + 'utf8.class', + 'lib/html2text' // see #1485505 + ), $classname ); include_once $filename. '.php'; diff --git a/program/include/main.inc b/program/include/main.inc index c6b4168..d308d93 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: main.inc 1459 2008-05-30 19:55:28Z alec $ + $Id: main.inc 2187 2008-12-24 14:19:27Z thomasb $ */ @@ -70,17 +70,7 @@ function get_sequence_name($sequence) $opt = rcmail::get_instance()->config->get($config_key); if (!empty($opt)) - { - $db = &rcmail::get_instance()->db; - - if($db->db_provider=='pgsql') // just for sure - { - $db->db_handle->setOption('disable_smart_seqname', true); - $db->db_handle->setOption('seqname_format', '%s'); - } - - return $CONFIG[$opt]; - } + return $opt; return $sequence; } @@ -100,51 +90,6 @@ function rcube_label($p) } -/** - * Load virtuser table in array - * - * @return array Virtuser table entries - */ -function rcmail_getvirtualfile() - { - global $CONFIG; - if (empty($CONFIG['virtuser_file']) || !is_file($CONFIG['virtuser_file'])) - return FALSE; - - // read file - $a_lines = file($CONFIG['virtuser_file']); - return $a_lines; - } - - -/** - * Find matches of the given pattern in virtuser table - * - * @param string Regular expression to search for - * @return array Matching entries - */ -function rcmail_findinvirtual($pattern) - { - $result = array(); - $virtual = rcmail_getvirtualfile(); - if ($virtual==FALSE) - return $result; - - // check each line for matches - foreach ($virtual as $line) - { - $line = trim($line); - if (empty($line) || $line{0}=='#') - continue; - - if (eregi($pattern, $line)) - $result[] = $line; - } - - return $result; - } - - /** * Overwrite action variable * @@ -169,35 +114,10 @@ function rcmail_overwrite_action($action) function rcmail_url($action, $p=array(), $task=null) { $app = rcmail::get_instance(); - - $qstring = ''; - $base = $app->comm_path; - - if ($task && in_array($task, rcmail::$main_tasks)) - $base = ereg_replace('_task=[a-z]+', '_task='.$task, $app->comm_path); - - if (is_array($p)) - foreach ($p as $key => $val) - $qstring .= '&'.urlencode($key).'='.urlencode($val); - - return $base . ($action ? '&_action='.$action : '') . $qstring; + return $app->url((array)$p + array('_action' => $action, 'task' => $task)); } -/** - * Add a localized label to the client environment - * @deprecated - */ -function rcube_add_label() - { - global $OUTPUT; - - $arg_list = func_get_args(); - foreach ($arg_list as $i => $name) - $OUTPUT->add_label($name); - } - - /** * Garbage collector function for temp files. * Remove temp files older than two days @@ -227,19 +147,19 @@ function rcmail_temp_gc() * Garbage collector for cache entries. * Remove all expired message cache records */ -function rcmail_message_cache_gc() +function rcmail_cache_gc() { - global $DB, $CONFIG; - - // no cache lifetime configured - if (empty($CONFIG['message_cache_lifetime'])) - return; + $rcmail = rcmail::get_instance(); + $db = $rcmail->get_dbh(); // get target timestamp - $ts = get_offset_time($CONFIG['message_cache_lifetime'], -1); + $ts = get_offset_time($rcmail->config->get('message_cache_lifetime', '30d'), -1); - $DB->query("DELETE FROM ".get_table_name('messages')." - WHERE created < ".$DB->fromunixtime($ts)); + $db->query("DELETE FROM ".get_table_name('messages')." + WHERE created < " . $db->fromunixtime($ts)); + + $db->query("DELETE FROM ".get_table_name('cache')." + WHERE created < " . $db->fromunixtime($ts)); } @@ -254,7 +174,9 @@ function rcmail_message_cache_gc() */ function rcube_charset_convert($str, $from, $to=NULL) { - static $mbstring_loaded = null, $convert_warning = false; + static $mbstring_loaded = null; + static $mbstring_list = null; + static $convert_warning = false; $from = strtoupper($from); $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to); @@ -264,11 +186,14 @@ function rcube_charset_convert($str, $from, $to=NULL) return $str; $aliases = array( - 'UNKNOWN-8BIT' => 'ISO-8859-15', - 'X-UNKNOWN' => 'ISO-8859-15', - 'X-USER-DEFINED' => 'ISO-8859-15', - 'ISO-8859-8-I' => 'ISO-8859-8', - 'KS_C_5601-1987' => 'EUC-KR', + 'US-ASCII' => 'ISO-8859-1', + 'ANSI_X3.110-1983' => 'ISO-8859-1', + 'ANSI_X3.4-1968' => 'ISO-8859-1', + 'UNKNOWN-8BIT' => 'ISO-8859-15', + 'X-UNKNOWN' => 'ISO-8859-15', + 'X-USER-DEFINED' => 'ISO-8859-15', + 'ISO-8859-8-I' => 'ISO-8859-8', + 'KS_C_5601-1987' => 'EUC-KR', ); // convert charset using iconv module @@ -282,21 +207,29 @@ function rcube_charset_convert($str, $from, $to=NULL) } } - // settings for mbstring module (by Tadashi Jokagi) - if (is_null($mbstring_loaded)) { - if ($mbstring_loaded = extension_loaded("mbstring")) - mb_internal_encoding(RCMAIL_CHARSET); - } + if (is_null($mbstring_loaded)) + $mbstring_loaded = extension_loaded('mbstring'); + // convert charset using mbstring module if ($mbstring_loaded) { $aliases['UTF-7'] = 'UTF7-IMAP'; $aliases['WINDOWS-1257'] = 'ISO-8859-13'; - // return if convert succeeded - if (($out = mb_convert_encoding($str, ($aliases[$to] ? $aliases[$to] : $to), ($aliases[$from] ? $aliases[$from] : $from))) != '') - return $out; + if (is_null($mbstring_list)) { + $mbstring_list = mb_list_encodings(); + $mbstring_list = array_map('strtoupper', $mbstring_list); + } + + $mb_from = $aliases[$from] ? $aliases[$from] : $from; + $mb_to = $aliases[$to] ? $aliases[$to] : $to; + + // return if encoding found, string matches encoding and convert succeeded + if (in_array($mb_from, $mbstring_list) && in_array($mb_to, $mbstring_list)) + if (mb_check_encoding($str, $mb_from)) + if ($out = mb_convert_encoding($str, $mb_to, $mb_from)) + return $out; } @@ -369,7 +302,7 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) $is_iso_8859_1 = true; } if (!$enctype) - $enctype = $GLOBALS['OUTPUT_TYPE']; + $enctype = $OUTPUT->type; // encode for plaintext if ($enctype=='text') @@ -399,7 +332,7 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) $str = strip_tags($str); // avoid douple quotation of & - $out = preg_replace('/&([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr)); + $out = preg_replace('/&([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr)); return $newlines ? nl2br($out) : $out; } @@ -422,6 +355,9 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) } $xml_rep_table['"'] = '"'; + $js_rep_table['"'] = '\\"'; + $js_rep_table["'"] = "\\'"; + $js_rep_table["\\"] = "\\\\"; } // encode for XML @@ -434,7 +370,7 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) if ($charset!='UTF-8') $str = rcube_charset_convert($str, RCMAIL_CHARSET,$charset); - return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), addslashes(strtr($str, $js_rep_table))); + return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table)); } // no encoding given -> return original string @@ -495,8 +431,11 @@ function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) $value = $_COOKIE[$fname]; } + // strip single quotes if magic_quotes_sybase is enabled + if (ini_get('magic_quotes_sybase')) + $value = str_replace("''", "'", $value); // strip slashes if magic_quotes enabled - if ((bool)get_magic_quotes_gpc()) + else if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) $value = stripslashes($value); // remove HTML tags if not allowed @@ -514,9 +453,10 @@ function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) * Remove all non-ascii and non-word chars * except . and - */ -function asciiwords($str) +function asciiwords($str, $css_id = false) { - return preg_replace('/[^a-z0-9.-_]/i', '', $str); + $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : ''); + return preg_replace("/[^$allowed]/i", '', $str); } /** @@ -543,22 +483,6 @@ function strip_newlines($str) } -/** - * Check if a specific template exists - * - * @param string Template name - * @return boolean True if template exists - */ -function template_exists($name) - { - global $CONFIG; - $skin_path = $CONFIG['skin_path']; - - // check template file - return is_file("$skin_path/templates/$name.html"); - } - - /** * Create a HTML table based on the given data * @@ -570,65 +494,46 @@ function template_exists($name) */ function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) { - global $DB; - - // allow the following attributes to be added to the
    tag - $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary')); + global $RCMAIL; - $table = '\n"; + $table = new html_table(/*array('cols' => count($a_show_cols))*/); - // add table title - $table .= "\n"; - + // add table header foreach ($a_show_cols as $col) - $table .= '\n"; - - $table .= "\n\n"; + $table->add_header($col, Q(rcube_label($col))); $c = 0; if (!is_array($table_data)) + { + $db = $RCMAIL->get_dbh(); + while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) { - while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data))) - { - $zebra_class = $c%2 ? 'even' : 'odd'; - - $table .= sprintf(''."\n", $sql_arr[$id_col]); + $zebra_class = $c % 2 ? 'even' : 'odd'; + $table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => "contact $zebra_class")); // format each col foreach ($a_show_cols as $col) - { - $cont = Q($sql_arr[$col]); - $table .= '\n"; - } - - $table .= "\n"; + $table->add($col, Q($sql_arr[$col])); + $c++; - } } + } else - { + { foreach ($table_data as $row_data) - { - $zebra_class = $c%2 ? 'even' : 'odd'; - - $table .= sprintf(''."\n", $row_data[$id_col]); + { + $zebra_class = $c % 2 ? 'even' : 'odd'; + $table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => "contact $zebra_class")); // format each col foreach ($a_show_cols as $col) - { - $cont = Q($row_data[$col]); - $table .= '\n"; - } - - $table .= "\n"; + $table->add($col, Q($row_data[$col])); + $c++; - } } + } - // complete message table - $table .= "
    ' . Q(rcube_label($col)) . "
    ' . $cont . "
    ' . $cont . "
    \n"; - - return $table; + return $table->show($attrib); } @@ -661,7 +566,8 @@ function rcmail_get_edit_field($col, $value, $attrib, $type='text') // use value from post if (!empty($_POST[$fname])) - $value = get_input_value($fname, RCUBE_INPUT_POST); + $value = get_input_value($fname, RCUBE_INPUT_POST, + $type == 'textarea' && strpos($attrib['class'], 'mce_editor')!==false ? true : false); $out = $input->show($value); @@ -669,29 +575,6 @@ function rcmail_get_edit_field($col, $value, $attrib, $type='text') } -/** - * Return the mail domain configured for the given host - * - * @param string IMAP host - * @return string Resolved SMTP host - */ -function rcmail_mail_domain($host) - { - global $CONFIG; - - $domain = $host; - if (is_array($CONFIG['mail_domain'])) - { - if (isset($CONFIG['mail_domain'][$host])) - $domain = $CONFIG['mail_domain'][$host]; - } - else if (!empty($CONFIG['mail_domain'])) - $domain = $CONFIG['mail_domain']; - - return $domain; - } - - /** * Replace all css definitions with #container [def] * and remove css-inlined scripting @@ -706,7 +589,8 @@ function rcmail_mod_css_styles($source, $container_id, $base_url = '') $last_pos = 0; // ignore the whole block if evil styles are detected - if (stristr($source, 'expression') || stristr($source, 'behavior')) + $stripped = preg_replace('/[^a-z\(:]/', '', rcmail_xss_entitiy_decode($source)); + if (preg_match('/expression|behavior|url\(|import/', $stripped)) return ''; // cut out all contents between { and } @@ -724,40 +608,44 @@ function rcmail_mod_css_styles($source, $container_id, $base_url = '') array( '/(^\s*\s*$)/', '/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im', - '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/ime', - '/<>/e', - "/$container_id\s+body/i" + "/$container_id\s+body/i", ), array( '', "\\1#$container_id \\2", - "sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url('\\2','$base_url')), urlencode($container_id))", - "\$a_css_values[\\1]", - "$container_id div.rcmBody" + "$container_id div.rcmBody", ), $source); + + // replace all @import statements to modify the imported CSS sources too + $styles = preg_replace_callback( + '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/im', + create_function('$matches', "return sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url(\$matches[2],'$base_url')), urlencode('$container_id'));"), + $styles); + + // put block contents back in + $styles = preg_replace_callback( + '/<>/', + create_function('$matches', "\$values = ".var_export($a_css_values, true)."; return \$values[\$matches[1]];"), + $styles); return $styles; } + /** - * Try to autodetect operating system and find the correct line endings + * Decode escaped entities used by known XSS exploits. + * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples * - * @return string The appropriate mail header delimiter + * @param string CSS content to decode + * @return string Decoded string */ -function rcmail_header_delm() +function rcmail_xss_entitiy_decode($content) { - global $CONFIG; - - // use the configured delimiter for headers - if (!empty($CONFIG['mail_header_delimiter'])) - return $CONFIG['mail_header_delimiter']; - else if (strtolower(substr(PHP_OS, 0, 3)=='win')) - return "\r\n"; - else if (strtolower(substr(PHP_OS, 0, 3)=='mac')) - return "\r\n"; - else - return "\n"; + $out = html_entity_decode(html_entity_decode($content)); + $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', create_function('$matches', 'return chr(hexdec($matches[1]));'), $out); + $out = preg_replace('#/\*.*\*/#Um', '', $out); + return $out; } @@ -789,7 +677,7 @@ function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'st function parse_attrib_string($str) { $attrib = array(); - preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]+)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER); + preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER); // convert attributes to an associative array (name => value) if ($regs) @@ -820,10 +708,10 @@ function format_date($date, $format=NULL) $ts = $date; else if (!empty($date)) { - while (($ts = @strtotime($date))===false) + // if date parsing fails, we have a date in non-rfc format. + // remove token from the end and try again + while ((($ts = @strtotime($date))===false) || ($ts < 0)) { - // if we have a date in non-rfc format - // remove token from the end and try again $d = explode(' ', $date); array_pop($d); if (!$d) break; @@ -835,9 +723,13 @@ function format_date($date, $format=NULL) return ''; // get user's timezone - $tz = $CONFIG['timezone']; - if ($CONFIG['dst_active']) - $tz++; + if ($CONFIG['timezone'] === 'auto') + $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600; + else { + $tz = $CONFIG['timezone']; + if ($CONFIG['dst_active']) + $tz++; + } // convert time to user's timezone $timestamp = $ts - date('Z', $ts) + ($tz * 3600); @@ -883,6 +775,8 @@ function format_date($date, $format=NULL) // month name (long) else if ($format{$i}=='F') $out .= rcube_label('long'.strtolower(date('M', $timestamp))); + else if ($format{$i}=='x') + $out .= strftime('%x %X', $timestamp); else $out .= date($format{$i}, $timestamp); } @@ -919,19 +813,20 @@ function format_email_recipient($email, $name='') * * @param mixed Debug message or data */ -function console($msg) +function console() { - if (!is_string($msg)) - $msg = var_export($msg, true); + $msg = array(); + foreach (func_get_args() as $arg) + $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; if (!($GLOBALS['CONFIG']['debug_level'] & 4)) - write_log('console', $msg); + write_log('console', join(";\n", $msg)); else if ($GLOBALS['OUTPUT']->ajax_call) - print "/*\n $msg \n*/\n"; + print "/*\n " . join(";\n", $msg) . " \n*/\n"; else { print '
    ';
    -    print $msg;
    +    print join(";
    \n", $msg); print "
    \n"; } } @@ -954,17 +849,26 @@ function write_log($name, $line) $log_entry = sprintf("[%s]: %s\n", date("d-M-Y H:i:s O", mktime()), $line); - - if (empty($CONFIG['log_dir'])) - $CONFIG['log_dir'] = INSTALL_PATH.'logs'; - - // try to open specific log file for writing - if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a')) - { - fwrite($fp, $log_entry); - fclose($fp); + + if ($CONFIG['log_driver'] == 'syslog') { + if ($name == 'errors') + $prio = LOG_ERR; + else + $prio = LOG_INFO; + syslog($prio, $log_entry); + } else { + // log_driver == 'file' is assumed here + if (empty($CONFIG['log_dir'])) + $CONFIG['log_dir'] = INSTALL_PATH.'logs'; + + // try to open specific log file for writing + if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a')) { + fwrite($fp, $log_entry); + fflush($fp); + fclose($fp); } } +} /** @@ -1002,68 +906,83 @@ function rcube_print_time($timer, $label='Timer') * @return string HTML code for the gui object */ function rcmail_mailbox_list($attrib) - { - global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH; - static $s_added_script = FALSE; +{ + global $RCMAIL; static $a_mailboxes; + + $attrib += array('maxlength' => 100, 'relanames' => false); // add some labels to client - rcube_add_label('purgefolderconfirm'); - rcube_add_label('deletemessagesconfirm'); - -// $mboxlist_start = rcube_timer(); + $RCMAIL->output->add_label('purgefolderconfirm', 'deletemessagesconfirm'); $type = $attrib['type'] ? $attrib['type'] : 'ul'; - $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') : - array('style', 'class', 'id'); - + unset($attrib['type']); + if ($type=='ul' && !$attrib['id']) $attrib['id'] = 'rcmboxlist'; - // allow the following attributes to be added to the