Page MenuHomePhorge

No OneTemporary

Size
184 KB
Referenced Files
None
Subscribers
None
diff --git a/CHANGELOG b/CHANGELOG
index b1f9ce1cf..65b2dcbc8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,1407 +1,1410 @@
CHANGELOG Roundcube Webmail
===========================
-- Fix bug where domain name was converted to lower-case even with login_lc=false (#1488593)
- Rewritten test scripts for PHPUnit
-- Fix lower-casing email address on replies (#1488598)
-- Fix line separator in exported messages (#1488603)
-- Fix XSS issue where plain signatures wasn't secured in HTML mode (#1488613)
-- Fix XSS issue where href="javascript:" wasn't secured (#1488613)
-- Fix impossible to create message with empty plain text part (#1488610)
-- Fix stripped apostrophes when replying in plain text to HTML message (#1488606)
-- Fix inactive Save search option after advanced search (#1488607)
-- Fix Remove from group option is active for contact search result (#1488608)
-- Disable autocapitalization in login form on iPad/iPhone (#1488609)
-- Fix focus on the list when list row is clicked (#1488600)
-- Added separate From and To columns apart from smart From/To column (#1486891)
-- Fix fallback to Larry skin when configured skin isn't available (#1488591)
-- Fix (workaround) delete operations with some versions of memcache (#1488592)
-- Fix (disable) request validation for spell and spell_html actions
- Add new DB abstraction layer based on PHP PDO, supporting SQLite3 (#1488332)
- Removed PEAR::MDB2 package
- Removed users.alias column, added option ('user_aliases')
to use email address from identities as username (#1488581)
- Removed redundant cache.cache_id column (#1488528)
- Fix order of attachments in sent mail (#1488423)
- Fix Shift + delete button does not permanently delete messages (#1488243)
- Add Content-Length for attachments where possible (#1485478)
- Fix attachment sizes in message print page and attachment preview page (#1488515)
- Add mail attachments using drag & drop on HTML5 enabled browsers
- Add workaround for invalid BODYSTRUCTURE response - parse message with Mail_mimeDecode package (#1485585)
- Display Tiff as Jpeg in browsers without Tiff support (#1488452)
- Don't display Pdf/Tiff/Flash attachments inline without browser support (#1488452, #1487929)
- Add is_escaped attribute for html_select and html_textarea (#1488485)
- Fix issue where draft auto-save wasn't executed after some inactivity time
- Add vCard import from multiple files at once (#1488015)
- Roundcube Framework:
Add possibility to replace IMAP driver with custom class
Add IMAP auto-connection feature, improving performance with caching enabled
Replace imap_init hook with storage_init (with additional 'driver' argument)
Improved performance by caching IMAP server's capabilities in session
Unified global functions naming (rcube_ prefix)
Move global functions from main.inc and rcube_shared.inc into classes
Better classes separation
+RELEASE 0.8.1
+-------------
+- Fix bug where domain name was converted to lower-case even with login_lc=false (#1488593)
+- Fix lower-casing email address on replies (#1488598)
+- Fix line separator in exported messages (#1488603)
+- Fix XSS issue where plain signatures wasn't secured in HTML mode (#1488613)
+- Fix XSS issue where href="javascript:" wasn't secured (#1488613)
+- Fix impossible to create message with empty plain text part (#1488610)
+- Fix stripped apostrophes when replying in plain text to HTML message (#1488606)
+- Fix inactive Save search option after advanced search (#1488607)
+- Fix Remove from group option is active for contact search result (#1488608)
+- Disable autocapitalization in login form on iPad/iPhone (#1488609)
+- Fix focus on the list when list row is clicked (#1488600)
+- Added separate From and To columns apart from smart From/To column (#1486891)
+- Fix fallback to Larry skin when configured skin isn't available (#1488591)
+- Fix (workaround) delete operations with some versions of memcache (#1488592)
+- Fix (disable) request validation for spell and spell_html actions
+
RELEASE 0.8.0
-------------
- Don't show product version on login screen (can be enabled by config)
- Renamed old default skin to 'classic'. Larry is the new default skin.
- Support connections to memcached socket file (#1488577)
- Enable TinyMCE inlinepopups plugin
- Update to TinyMCE 3.5.6
- Correctly escape localized labels in javascript variable (#1488567)
- Update Net_SMTP/Auth_SASL packages to fix Digest-MD5/Cram-MD5 authentication (#1488571)
- Don't add attachments content into reply/forward/draft message body (#1488557)
- Fix 'no connection' errors on page unloads (#1488547)
- Plugin API: Add 'unauthenticated' hook (#1488138)
- Show explicit error message when provided hostname is invalid (#1488550)
- Fix wrong compose screen elements focus in IE9 (#1488541)
- Fix fatal error when date.timezone isn't set (#1488546)
- Update to TinyMCE 3.5.4.1
- Better icons with distinct shapes for priority columns (#1488377)
- Show dedicated icon for multipart/report messages (#1488524)
- Properly hide text of icon links/buttons (#1488534)
- Fix handling of unitless CSS size values in HTML message (#1488535)
- Fix removing contact photo using LDAP addressbook (#1488420)
- Fix storing X-ANNIVERSARY date in vCard format (#1488527)
- Update to Mail_Mime-1.8.5 (#1488521)
- Fix XSS vulnerability in message subject handling using Larry skin (#1488519)
- Fix handling of links with various URI schemes e.g. "skype:" (#1488106)
- Fix handling of links inside PRE elements on html to text conversion
- Fix indexing of links on html to text conversion
- Decode header value in rcube_mime::get() by default (#1488511)
- Fix errors with enabled PHP magic_quotes_sybase option (#1488506)
- Fix SQL query for contacts listing on MS SQL Server (#1488505)
- Fix window.resize handler on IE8 and Opera (#1488453)
- Don't let error message popups cover the login form (#1488500)
- Update to TinyMCE 3.5.2
- Don't show errors when moving contacts into groups they are already in (#1488493)
- Make folders with unread messages in subfolders bold again (#1486793)
- Abbreviate long attachment file names with ellipsis (#1488499)
- Fix html2text conversion of strong|b|a|th|h tags when used in upper case
- Add listcontrols template container in Larry skin (#1488498)
- Fix host autoselection when default_host is an array (#1488495)
- Move messages forwarding mode setting into Preferences
- Fix HTML entities handling in HTML editor (#1488483)
- Fix listing shared folders on Courier IMAP (#1488466)
RELEASE 0.8-rc
--------------
- Added new translations in Belarusian, Interlingua and Malayalam
- Flipped compose options arrow (#1488474)
- Fix handling of large uuencode attachments (#1488473)
- Fix handling of "usemap" attribute (#1488472)
- Fix handling of some HTML tags e.g. IMG (#1488471)
- Use similar language as a fallback for plugin localization (#1488401)
- Fix issue where signature wasn't re-added on draft compose (#1488322)
- Update to TinyMCE 3.5 (#1488459)
- Fixed multi-threaded autocompletion when number of threads > number of sources
- Allow to configure the number of values allowed for each LDAP attribute
- Support for serialized LDAP address values (usually delimited with a $)
- Less restrictive session auth checks, repeat keep-alive requests on failure (#1488449)
- Fix redirect to mail/compose on re-login (#1488226)
- Add IE8 hack for messages list issue (#1487821)
- Fix handling errors on draft auto-save
- Fix importing vCard photo with ENCODING param specified (#1488432)
- Support mutliple name/email pairs for Bcc and Reply-To identity settings (#1488445)
- Set flexible width to login form fields (#1488418)
- Fix re-draw bug on list columns change in IE8 (#1487822)
- Allow mass-removal of addresses from a group (#1487748)
- Fix removing all contacts on import to LDAP addressbook
- Fix so "Back" from compose/show doesn't reset search request (#1488238)
- Add option to delete messages instead of moving to Trash when in Junk folder (#1486686)
- Fix invisible cursor when replying to a html message (#1487073)
- Reset IP stored in session when destroying session data (#1488056)
- Fix bug where memory_limit = -1 wasn't handled properly
- Support LDAP RFC2256's country object class read/write (#1488123)
- Upgraded to jQuery 1.7.2
- Image resize with GD extension (#1488383)
- Fix lack of warning when switching task in compose window (#1488399)
- Fix bug where it wasn't possible to enter ( or & characters in autocomplete fields
- Request all needed fields from address book backends (#1488394)
- Unified (single) spellchecker button
- Scroll long lists on drag&drop (#1485946)
- Copy all skins in installto script (#1488376)
RELEASE 0.8-beta
----------------
- Upgraded to jQuery 1.7.1 (#1488337) and jQuery UI 1.8.18
- Add Russian to the spellchecker languages list (#1488135)
- Remember custom skin selection after logout (#1488355)
- Make sure About tab is always the last tab (#1488257)
- Fix issue with folder creation under INBOX. namespace (#1488349)
- Added mailto: protocol handler registration link in User Preferences (#1486580)
- Handle identity details box with an iframe (#1487020)
- Fix issue where some text from original message was missing on reply (#1488340)
- Fix autoselect_host() for login (#1488297)
- Changed license to GNU GPLv3+ with exceptions for skins & plugins
- Added address book widget on compose screen
- Use proper timezones from PHP's internal timezonedb (#1485592)
- Add separate pagesize setting for mail messages and contacts (#1488269)
- Deprecate $DB, $USER, $IMAP global variables, Use $RCMAIL instead
- Add option to set default font for HTML message (#1484137)
- Fix issues with big memory allocation of IMAP results
- Prevent from memory_limit exceeding when trying to parse big messages bodies (#1487424)
- Add possibility to add SASL mechanisms for SMTP in smtp_connect hook (#1487937)
- Mark (with different color) folders with recent messages (#1486234)
- Added About tab in Settings
- TinyMCE updated to 3.4.6
RELEASE 0.7.2
-------------
- Fix encoding of attachment with comma in name (#1488389)
- Fix handling of % character in IMAP protocol (#1488382)
- Fix duplicate names handling in addressbook searches (#1488375)
- Fix displaying of HTML messages from Disqus (#1488372)
- Disable E_STRICT warnings on PHP 5.4
- Prevent from folder selection on virtual folder collapsing (#1488346)
- Fix automatic unsubscribe of non-existent folders
- Fix double-quotes handling in recipient names
- User configurable setting how to display contact names in list
- Make contacts list sorting configurable for the admin/user
- Fix parse errors in DDL files for MS SQL Server
- Revert SORT=DISPLAY support, removed by mistake (#1488327)
- Add lost translation label in de_DE (#1488315)
- Fix drafts update issues when edited from preview pane (#1488314)
- Fix wrong variable name in rcube_ldap.php (#1488302)
- Make mime type detection based on filename extension to be case-insensitive
- Fix failure on MySQL database upgrade from 0.7 - text column can't have default value (#1488300)
RELEASE 0.7.1
-------------
- Fix bug in handling of base href and inline content (#1488290)
- Fix SQL Error when saving a contact with many email addresses (#1488286)
- Fix strict email address searching if contact has more than one address
- Remove duplicated 'organization' label (#1488287)
- Fix so editor selector is hidden when 'htmleditor' is listed in 'dont_override'
- Fix wrong (long) label usage (#1488283)
- Fix handling of INBOX's subfolders in special folders config (#1488279)
- Add ifModule statement for setting Options -Indexes in .htaccess file (#1488274)
- Fix crashes with eAccelerator (#1488256)
- Fix searching on IMAP servers without CHARSET specifier support (#1488271)
- Fix expanding folders during drag&drop (#1488260)
- Fix wrong postgres sequence name in upgrade from 0.6
- Fix broken CREATE INDEX queries in SQLite DDL files (#1488255)
RELEASE 0.7
-----------
- Make Roundcube render the Email Standards Project Acid Test correctly
- Replace prompt() with jQuery UI dialog (#1485135)
- Fix navigation in messages search results
- Improved handling of some malformed values encoded with quoted-printable (#1488232)
- Add possibility to do LDAP bind before searching for bind DN
- Fix handling of empty <U> tags in HTML messages (#1488225)
- Add content filter for embedded attachments to protect from XSS on IE (#1487895)
- Use strpos() instead of strstr() when possible (#1488211)
- Fix handling HTML entities when converting HTML to text (#1488212)
- Fix fit_string_to_size() renders browser and ui unresponsive (#1488207)
- Fix handling of invalid characters in request (#1488124)
- Fix merging some configuration options in update.sh script (#1485864)
- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#1488208)
- Fix handling contact photo url with https:// prefix (#1488202)
- Fix possible infinite redirect on attachment preview (#1488199)
- Improved clickjacking protection for browsers which don't support X-Frame-Options headers
- Fixed bug where similar folder names were highlighted wrong (#1487860)
- Fixed bug in handling link with '!' character in it (#1488195)
- Fixed bug where session ID's length was limited to 40 characters (#1488196)
- TinyMCE security issue: removed moxieplayer (embedding flv and mp4 is not supported anymore)
RELEASE 0.7-beta
----------------
- Fix handling of HTML form elements in messages (#1485137)
- Fix regression in setting recipient to self when replying to a Sent message (#1487074)
- Fix listing of folders in hidden namespaces (#1486796)
- Don't consider \Noselect flag when building folders tree (#1488004)
- Fix sorting autocomplete results (#1488084)
- Add option to set session name (#1486433)
- Add option to skip alternative email addresses in autocompletion
- Fix inconsistent behaviour of Compose button in Drafts folder, add Edit button for drafts
- Fix problem with parsing HTML message body with non-unicode characters (#1487813)
- Add option to define matching method for addressbook search (#1486564, #1487907)
- Make email recipients separator configurable
- Fix so folders with \Noinferiors attribute aren't listed in parent selector
- Fix handling of curly brackets in URLs (#1488168)
- Fix handling of dates (birthday/anniversary) in contact data (#1488147)
- Fix error on opening searched LDAP contact (#1488144)
- Fix redundant line break in flowed format (#1488146)
- Fix IDN address validation issue (#1488137)
- Fix JS error when dst_active checkbox doesn't exist (#1488133)
- Autocomplete LDAP records when adding contacts from mail (#1488073)
- Plugin API: added 'ready' hook (#1488063)
- Ignore DSN request when it isn't supported by SMTP server (#1487800)
- Make sure LDAP name fields aren't arrays (#1488108)
- Fixed imap test to non-default port when using ssl (#1488118)
- Force all files to be overwritten when updating (#1488117)
- Fix issue where it wasn't possible to change list view mode in folder manager for INBOX (#1488107)
- Fix namespace handling in special folders settings (#1488112)
- Disable time limit for CLI scripts (#1488109)
- Fix misleading display when chaning editor type (#1488104)
- Add loading indicator on contact delete
- Fix bug where after delete message rows can be added to the list of another folder (#1487752)
- Add notice on autocompletion that not all records were displayed
- Add option 'searchonly' for LDAP address books
- Add Priority filter to the messages list
- Cache synchronization using QRESYNC/CONDSTORE
- Trigger 'new_messages' hook for all checked folders (#1488083)
- Make date/time format user configurable; drop 'date_today' config option
- Fix setting title for truncated subject in IE (#1487128)
- Fix displaying multipart/alternative messages with only one part (#1487938)
- Rewritten messages caching:
Indexes are stored in a separate table, so there's no need to store all messages in a folder
Added threads data caching
Flags are stored separately, so flag change doesn't cause DELETE+INSERT, just UPDATE
- Improved FETCH response handling
- Improvements in response tokenization method
- Use 'From' and 'To' labels instead of 'Sender' and 'Recipient'
- Fix username case-insensitivity issue in MySQL (#1488021)
- Addressbook Saved Searches
- Added spellchecker exceptions dictionary (shared or per-user)
- Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options)
- Added 'priority' column on messages list (#1486782)
- Localize forwarded message header (#1488058)
RELEASE 0.6
-----------
- Fix bug where the last identity is used on reply (#1488101)
- Fix locked folder rename option on servers supporting RFC2086 only (#1488089)
- Fix session race conditions when composing new messages
- Fix encoding of LDAP contacts identifiers (#1488079)
- jQuery 1.6.4
- Fix handling of binary attachments encoded with quoted-printable (#1488065)
- Fix text-overflow:ellipsis issues on messages list in FF7 and Webkit (#1488061)
- Fix handling of links with IP address
- Fix compacting folder resets message list filter (#1488076)
RELEASE 0.6-rc
----------------
- Send X-Frame-Options headers to protect from clickjacking (#1487037)
- Fallback to mail_domain in LDAP variable replacements; added 'host' to 'user_create' hook arguments (#1488024)
- Fixed wrong vCard type parameter mobile (#1488067)
- Fixed vCard WORKFAX issue (#1488046)
- Add vCard's Profile URL support (#1488062)
- jQuery 1.6.3
- Fix imap_cache setting to values other than 'db' (#1488060)
- Fix handling of attachments inside message/rfc822 parts (#1488026)
- Make list of mimetypes that open in preview window configurable (#1487625)
- Added plugin hook 'message_part_get' for attachment downloads
- Added unique connection identifier to IMAP debug messages
- Fix image type check for contact photo uploads
RELEASE 0.6-beta
----------------
- Fixed selecting identity on reply/forward (#1487981)
- Add option to hide selected LDAP addressbook on the list
- Add client-side checking of uploaded files size
- Add newlines between organization, department, jobtitle (#1488028)
- Recalculate date when replying to a message and localize the cite header (#1487675)
- Fix handling of email addresses with quoted local part (#1487939)
- Fix EOL character in vCard exports (#1487873)
- Added optional "multithreading" autocomplete feature
- Plugin API: Added 'config_get' hook
- Fixed new_user_identity plugin to work with updated rcube_ldap class (#1487994)
- Plugin API: added folder_delete and folder_rename hooks
- Added possibility to undo last contact delete operation
- Fix sorting of contact groups after group create (#1487747)
- Add optional textual upload progress indicator (#1486039)
- Fix parsing URLs containing commas (#1487970)
- Added vertical splitter for books/groups list in addressbook (#1487923)
- Improved namespace roots handling in folder manager
- Added searching in all addressbook sources
- Added addressbook source selection in contacts import
- Implement LDAPv3 Virtual List View (VLV) for paged results listing
- Use 'address_template' config option when adding a new address block (#1487944)
- Added addressbook advanced search
- Add popup with basic fields selection for addressbook search
- Case-insensitive matching in autocompletion (#1487933)
- Added option to force spellchecking before sending a message (#1485458)
- Fix handling of "<" character in contact data, search fields and folder names (#1487864)
- Fix saving "<" character in identity name and organization fields (#1487864)
- Added option to specify to which address book add new contacts
- Added plugin hook for keep-alive requests
- Store user preferences in session when write-master is not available and session is stored in memcache, write them later
- Improve performence of folder manager operations
- Fix default_port option handling in Installer when config.inc.php file exists (#1487925)
- Removed option focus_on_new_message, added newmail_notifier plugin
- Added general rcube_cache class with Memcache and APC support
- Improved caching performance by skipping writes of unchanged data
- Option enable_caching replaced by imap_cache and messages_cache options
- Fix WORKFAX saving in address book (#1487910)
- Add forward-as-attachment feature
- jQuery-1.6.2 (#1487913, #1487144)
- Improve display name composition when saving contacts (#1487143)
- Fix problems with subfolders of INBOX folder on some IMAP servers (#1487725)
- Fix handling of folders that doesn't belong to any namespace (#1487637)
- Enable multiselection for attachments uploading in capable browsers (#1485969)
- Add possibility to change HTML editor configuration by skin
- Fix a bug where selecting too many contacts would produce too large URI request (#1487892)
- Improve performance by including files with absolute path (#1487849)
- Move folder name truncation to client/skin (#1485412)
- Added plugin hook for request token creation
- Replace LDAP vars in group queries (#1487837)
- Fix vcard folding with uncode characters (#1487868)
- Keep all submitted data if contact form validation fails (#1487865)
- Handle uncode strings in rcube_addressbook::normalize_string() (#1487866)
- Fix handling of debug_level=4 in ajax requests (#1487831)
- Enable TinyMCE's contextmenu (#1487014)
- Allow multiple concurrent compose sessions
- New config option for custom logo
- Allow skins to define/override texts with <roundcube:label />
- Add simple ACL rights/namespace handling in folder manager
- Force IE to send referers (#1487806)
- Better display of vcard import results (#1485457)
- Improved vcard import
- Interactive update script with improved DB schema check
- Fix problem with contactgroupmembers table creation on MySQL 4.x, add index on contact_id column
- Add LDAP SASL bind and proxy authentication (#1486692)
- Replying to a sent message puts the old recipient as the new recipient (#1487074)
- Fulltext search over (almost) all data for contacts
- Extend address book with rich contact information
RELEASE 0.5.4
-------------
- Fix XSS vulnerability in UI messages (#1488030)
RELEASE 0.5.3
-------------
- Fix identities "reply-to" and "bcc" fields have a bogus value when left empty (#1487943)
- Fix issue which cases IMAP disconnection when encrypt() method was used (#1487900)
- Fix some CSS issues in Settings for Internet Explorer
- Fixed handling of folder with name "0" in folder selector
- Fix bug where messages were deleted instead moved to trash folder after Shift key was used (#1487902)
- Fix relative URLs handling according to a <base> in HTML (#1487889)
- Fix handling of top-level domains with more than 5 chars or unicode chars (#1487883)
- Fix usage of non-standard HTTP error codes (#1487797)
- Fix PHP warning on mistaken in_array() usage (#1487901)
RELEASE 0.5.2
-------------
- TinyMCE 3.4.2 now compatible with IE9
- PEAR::Net_SMTP 1.5.2, fixed timeout issue (#1487843)
- Fix bug where template name without plugin prefix was used in render_page hook
- Support 'abort' and 'result' response in 'preferences_save' hook, add error handling
- Fix bug where some content would cause hang on html2text conversion (#1487863)
- Improve space-stuffing handling in format=flowed messages (#1487861)
- Fix bug where some dates would produce SQL error in MySQL (#1487856)
- Added workaround for some IMAP server with broken STATUS response (#1487859)
- Fix bug where default_charset was not used for text messages (#1487836)
- Stateless request tokens. No keep-alive necessary on login page (#1487829)
- Force names of unique constraints in PostgreSQL DDL
- Add code for prevention from IMAP connection hangs when server closes socket unexpectedly
- Remove redundant DELETE query (for old session deletion) on login
- Get around unreliable rand() and mt_rand() in session ID generation (#1486281)
- Fix some emails are not shown using Cyrus IMAP (#1487820)
- Fix handling of mime-encoded words with non-integral number of octets in a word (#1487801)
- Fix parsing links with non-printable characters inside (#1487805)
- Fixed de_CH Localization bugs (#1487773)
- Add variable for 'Today' label in date_today option (#1486120)
- Fix dont_override setting does not override existing user preferences (#1487664)
- Use only one from IMAP authentication methods to prevent login delays (1487784)
- Support strftime format in date_today option
- Fix SQL query in rcube_user::query() so it uses index on MySQL again
- Removed redundant </form> tags from contact add/edit pages
- Fix CSS error in contact details screen on IE7 (#1487775)
RELEASE 0.5.1
-------------
- Fix handling of attachments with invalid content type (#1487767)
- Add workaround for DBMail's bug http://www.dbmail.org/mantis/view.php?id=881 (#1487766)
- Use IMAP's ID extension (RFC2971) to print more info into debug log
- Security: add optional referer check to prevent CSRF in GET requests
- Fix email_dns_check setting not used for identities/contacts (#1487740)
- Fix ICANN example addresses doesn't validate (#1487742)
- Security: protect login form submission from CSRF
- Security: prevent from relaying malicious requests through modcss.inc
- Fix handling of non-image attachments in multipart/related messages (#1487750)
- Fix IDNA support when IDN/INTL modules are in use (#1487742)
- Fix handling of invalid HTML comments in messages (#1487759)
- Fix parsing FETCH response for very long headers (#1487753)
- Fix add/remove columns in message list when message_sort_order isn't set (#1487751)
- Check mime headers before attempt to parse them (#1487745)
- Quote header values in show_additional_headers plugin (#1487744)
- Fix settings UI on IE 6 (#1487724)
- Remove double borders in folder listing (#1487713)
- Separate full message headers UI element from headers table (#1487715)
- Add part MIME ID to message_part_* hooks (#1487718)
- Improve parsing of MS Outlook vCards (#1487716)
- Updated PEAR::Net_Socket to 1.0.10
- Updated PEAR::Net_IDNA2 to 0.1.1
- Fix handling of comments inside an email address spec. (#1487673)
- Show full mail subject as title when hovering a cut subject link (#1487128)
- Fix randomly disappearing folders list in IE (#1487704)
- Fix list column add/removal in IE (#1487703)
- Fix login redirect issues (#1487686)
- Require PHP 5.2.1 or greater
- Fix %h/%z variables in username_domain option (#1487701)
- Workaround for setting charset in case of malformed bodystructure response (#1487700)
- Fix impossible to subscribe to protected folders (#1487656)
- Fix setting timezone in Preferences (#1487705)
RELEASE 0.5
-----------
- Fix double-login/session issue (#1487104)
- Wrap HTML parts with <html><body> and add Doctype declaration (#1487098)
- Make rcube_autoload silently skip unknown classes (#1487109)
- Fix charset detection in vcards with encoded values (#1485542)
- Better CSS cursors for splitters (#1486874)
- Show the same message only once (#1487641)
- Fix namespaces handling (#1487649)
- Add handling of multifolder METADATA/ANNOTATION responses
- Fix handling of INBOX when personal namespace prefix is non-empty (#1487657)
- Fix handling square brackets in links (#1487672)
- Add description of 'use_https' option in main.inc.php.dist file
RELEASE 0.5-RC
--------------
- Plugin API: Add 'pass' argument in 'authenticate' hook (#1487134)
- Fix attachments of type message/rfc822 are not listed on attachments list
- Add 'login_lc' config option for case-insensitive authentication (#1487113)
- Fix window is blur'ed in IE when selecting a message (#1487316)
- Fix cursor position on compose form in Webkit browsers (#1486674)
- Fix setting charset of attachment filenames (#1487122)
- Allow setting autocomplete attribute for all inputs separately (#1487313)
- New Folder Manager UI
- Fix invalid Request when creating a folder (#1487443)
- Add folder size and quota indicator in folder manager (#1485780)
- Add possibility to move a subfolder into root folder (#1486791)
- Fix copying all messages in a folder copies only messages from current page
- Improve performance of moving or copying of all messages in a folder
- Fix plaintext versions of HTML messages don't contain placeholders for emotions (#1485206)
- Improve performance of folder rename and delete actions
- Better support for READ-ONLY and NOPERM responses handling (#1487083)
- Add confirmation message on purge/expunge command response
- Fix handling of untagged responses for AUTHENTICATE command (#1487450)
- Add username and IP address to log message on unsuccessful login (#1487626)
- Improved Mail-Followup-To and Mail-Reply-To headers handling
- Fix charset conversion for text attachments without charset specification (#1487634)
RELEASE 0.5-BETA
----------------
- Make session data storage more robust against garbage session data (#1487136)
- Config option for autocomplete on login screen
- Allow plugin templates to include local files (#1487133)
- List groups in address detail view and allow to subscribe/unsubscribe from there (#1486753)
- Messages caching: performance improvements, fixed syncing, fixes related with #1486748
- Add link to identities in compose window (#1486729)
- Add Internationalized Domain Name (IDNA) support (#1483894)
- Add option to automatically send read notifications for known senders (#1485883)
- Add option to "Return receipt" will be always checked (#1486352)
- Fix HTML to plain text conversion doesn't handle citation blocks (#1486921)
- Use custom sorting when SORT is disabled by IMAP admin (#1486959)
- Allow setting some washtml options from plugin (#1486578)
- Add option do bind for an individual LDAP address book (#1486997)
- Change reply prefix to display email address only if sender name doesn't exist (#1486550)
- Plugin API: improved 'abort' flag handling, added 'result' item in some hooks (#1486914)
- Fix mailto optional params in plain text messages aren't handled (#1487026)
- Add Reply-to-List feature (#1484252)
- Add Mail-Followup-To/Mail-Reply-To support (#1485547)
- Fix confirmation message isn't displayed after sending mail on Chrome (#1486177)
- Fix keyboard doesn't work with autocomplete list with Chrome (#1487029)
- Improve tabs to fixed width and add tabs in identities info (#1486974)
- Add unique index on users.username+users.mail_host
- Make htmleditor option more consistent and add option to use HTML on reply to HTML message (#1485840)
- Use empty envelope sender address for message disposition notifications (RFC 2298.3)
- Support SMTP Delivery Status Notifications - RFC 3461 (#1486142)
- Use css sprite image for messages list
- Add (different) attachment icon for messages of type multipart/report (#1486165)
- Prevent from inserting empty link when composing HTML message (#1486944)
- Add caching support in id2uid and uid2id functions (#1487019)
- Add SASL proxy authentication for SMTP (#1486693)
- Improve displaying of UI messages (#1486977)
- Fix double e-mail filed in identity form (#1487054)
- Display IMAP errors for LIST/THREAD/SEARCH commands (#1486905)
- Add LITERAL+ (IMAP4 non-synchronizing literals) support (RFC 2088)
- Add separate column for message status icon (#1486665)
- Add ACL extension support into IMAP classes (RFC 4314)
- Add ANNOTATEMORE extension support into IMAP classes (draft-daboo-imap-annotatemore)
- Add METADATA extension support into IMAP classes (RFC 5464)
- Fix decoding of e-mail address strings in message headers (#1487068)
- Fix handling of attachments when Content-Disposition is not inline nor attachment (#1487051)
- Improve performance of unseen messages counting (#1487058)
- Improve performance of messages counting using ESEARCH extension (RFC4731)
- Add LIST-STATUS support in rcube_imap_generic class (RFC 5819)
- Add SASL-IR support in IMAP (RFC 4959)
- Add LOGINDISABLED support (RFC 2595)
- Add support for AUTH=PLAIN in IMAP authentication
- Re-implemented SMTP proxy authentication support
- Add support for IMAP proxy authentication (#1486690)
- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)
- Fix parent folder with unread subfolder not bold when message is open (#1487078)
- Add basic IMAP LIST's \Noselect option support
- Add support for selection options from LIST-EXTENDED extension (RFC 5258)
- Don't list subscribed but non-existent folders (#1486225)
- Fix handling of URLs with tilde (~) or semicolon (;) character (#1487087, #1487088)
- Plugin API: added 'contact_form' hook
- Add SORT=DISPLAY support (RFC 5957)
- Plugin API: add possibility to disable plugin in AJAX mode, 'noajax' property
- Plugin API: add possibility to disable plugin in framed mode, 'noframe' property
- Improve performance of setting IMAP flags using .SILENT suffix
- Improve performance of message cache status checking with skip_disabled=true
- Support contact's email addresses up to 255 characters long (#1487095)
- Add option to place replies in the folder of the message being replied to (#1485945)
- Add missing confirmation/error messages on contact/group/message actions (#1486845)
- Add 'loading' message on message move/copy/delete/mark actions
- Improve responsiveness of messages displaying (#1486986)
- Add option for minimum length of autocomplete's string (#1486428)
- Fix operations on messages in unsubscribed folder (#1487107)
- Add support for shared folders (#1403507)
- Fix handling of folders with name "0" (#1487119)
- Fix handling of folders with "<>" characters in name
- jQuery 1.4.4
- Fix handling of HTML entity strings in plain text messages
- Fix focused elements aren't unfocused when clicking on the list (#1487123)
- Fix error in MSSQL DDL scripts (#1487112)
- Lock submit button in onsubmit event on login page (#1487036)
- Don't set attachment's charset in Content-type header (#1487122)
- Fix handling of message bodies (quoted-printable encoded) with NULL characters (#1486189)
- Add workaround for MSOE's multipart/related messages with non-related attachments
RELEASE 0.4.2
-------------
- Fix handling of backslash as IMAP delimiter
- Fix charset replacement in HTML message bodies (#1487021)
- Fix: contact group input is empty when using rename action more than once on the same group record
- Fix "Server Error! (Not Found)" when using utils/save-pref action (#1487023)
- Fix handling of Thunderbird's vCards (#1487024)
RELEASE 0.4.1
-------------
- Fix space-stuffing in format=flowed messages (#1487018)
- Fix msgexport.sh now using the new imap wrapper
- Avoid displaying password on shell (#1486947)
- Only lower-case user name if first login attempt failed (#1486393)
- Make alias setting in squirrelmail_usercopy plugin configurable (patch by pommi, #1487007)
- Prevent from saving a non-existing skin path in user prefs (#1486936)
- Improve handling of single-part messages with bogus BODYSTRUCTURE (#1486898)
- Fix path to SQL files when using pgsql/mysqli/sqlsrv drivers (#1486902)
- Fix upgrade script for SQLite (#1486903)
- Fixes in SQL init script + added update script for MSSQL database
- Remove redundant date in syslog messages (#1486945)
- Fix contacts list page controls when a group is selected (#1486946)
- Fix SMTP test in Installer (#1486952)
- Fix "Select all" causes message to be opened in folder with exactly one message (#1486913)
- Fix Tab key doesn't work in HTML editor in Google Chrome (#1486925)
- Fix TinyMCE uses zh_CN when zh_TW locale is set (#1486929)
- Fix TinyMCE buttons are hidden in Opera (#1486922)
- Fix JS error on IE when trying to send HTML message with enabled spellchecker (#1486940)
- Display inline images with known extensions and non-image content-type (#1486934)
- Fix "Threaded" checkbox after subfolder creation (#1486928)
- Fix timezone string in sent mail (#1486961)
- Show disabled checkboxes for protected folders instead of dots (#1485498)
- Added fieldsets in Identity form, added 'identity_form' hook
- Re-added 'Close' button in upload form (#1486930, #1486823)
- Fix handling of charsets with LATIN-* label
- Fix messages background image handling in some cases (#1486990)
- Fix format=flowed handling (#1486989)
- Fix when IMAP connection fails in 'get' action session shouldn't be destroyed (#1486995)
- Fix list_cols is not updated after column dragging (#1486999)
- Support %z variable in host configuration options (#1487003)
RELEASE 0.4
-----------
- Fix disapearing upload form disapears when user selects a file on Safari (#1486823)
- Don't replace error messages with loading info (#1486300)
- Fix JS errors on compose mode switch (#1486870)
- Fix message structure parsing when it lacks optional fields (#1486881)
- Include all recipients in sendmail log
- Support HTTP_X_FORWARDED_PROTO header for HTTPS detecting (#1486866)
- Fix default IMAP port configuration (#1486864)
- Create Sent folder when starting to compose a new message (#1486802)
- Fix handling of messages with Content-Type: application/* and no filename (#1484050)
- Improved compose screen: resizable body and attachments list, vertical splitter, options menu
- Fix RC forgets search results (#1483883)
- TinyMCE 3.3.7
- Improve parsing of styled empty tags in HTML messages (#1486812)
- Add %dc variable support in base_dn/bind_dn config (#1486779)
- Add button to hide/unhide the preview pane (#1484215)
- Fix no-cache headers on https to prevent content caching by proxies (#1486798)
- Fix attachment filenames broken with TNEF decoder using long filenames (#1486795)
- Use user's timezone in Date header, not server's timezone (#1486119)
- Add option to set separate footer for HTML messages (#1486660)
- Add real SMTP error description to displayed error messages (#1485927)
- Fix some IMAP errors handling when opening the message (#1485443)
- Fix related parts aren't displayed when got mimetype other than image/* (#1486432)
- Multiple identity and database support for squirrelmail_usercopy plugin (#1486517)
- Support dynamic hostname (%d/%n) variables in configuration options (#1485438)
- Add 'messages_list' hook (#1486266)
- Add request* event triggers in http_post/http_request (#1486054)
- Fix use RFC-compliant line-delimiter when saving messages on IMAP (#1486712)
- Add 'imap_timeout' option (#1486760)
- Fix forwarding of messages with winmail attachments
- Fix handling of uuencoded attachments in message body (#1485839)
- Added list_mailboxes hook in rcube_imap::list_unsubscribed() (#1486668)
- Fix wrong message on file upload error (#1486725)
- Add support for data URI scheme [RFC2397] (#1486740)
- Added 'actionbefore', 'actionafter', 'responsebefore', 'responseafter' events
- Fix double-addition of e-mail domain to content ID in HTML images
- Read and send messages with format=flowed (#1484370), fixes word wrapping issues (#1486543)
- Fix duplicated attachments when forwarding a message (#1486487)
- Fix message/rfc822 attachments containing only attachments are not parsed properly (#1486743)
- Fix %00 character in winmail.dat attachments names (#1486738)
- Fix handling errors of folder deletion (#1486705)
- Parse untagged CAPABILITY response for LOGIN command (#1486742)
- Renamed all php-cli scripts to use .sh extension
- Some files from /bin + spellchecking actions moved to the new 'utils' task
- Added thread tree icons
- Extend contact groups support (#1486682)
- Fix check-recent action issues and performance (#1486526)
- Fix messages order after checking for recent (#1484664)
- Fix autocomplete shows entries without email (#1486452)
- Fix listupdate event doesn't trigger on search response (#1486708)
- Fix select_all_mode value after selecting a message (#1486720)
- Set focus to editor on reply in HTML mode (#1486632)
- Fix composing in HTML jumps cursor to body instead of recipients (#1486674)
- Allow columns order change per user - drag&drop (#1485795)
- Add References header in read receipt (#1486681)
- Fix database constraint violation when opening a message (#1486696)
- Add 'loading' message while login is in progress (#1486667)
- Fix quota_zero_as_unlimited (#1486662)
- Fix folder subscription checking (#1486684)
- Fix INBOX appears (sometimes) twice in mailbox list (#1486672)
- Fix listing of attachments of some types e.g. "x-epoc/x-sisx-app" (#1486653)
- Fix DB Schema checking when some db_table_* options are not set (#1486654)
RELEASE 0.4-beta
----------------
- Add sizelimit and timelimit variables in LDAP config (#1486544)
- Hide IMAP host dropdown when single host is defined (#1486326)
- Add images pre-loading on login page (#1451160)
- Add HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR to successful logins log (#1486441)
- Fix setting spellcheck languages with extended codes (#1486605)
- Fix messages list scrolling in FF3.6 (#1486472)
- Fix quicksearch input focus (#1486637)
- Always set changed date when flagging a DB record as deleted + provide a cleanup script
- Fix address book/group selection (#1486619)
- Assign newly created contacts to the active group (#1486626)
- Added option not to mark messages as read when viewed in preview pane (#1485012)
- Allow plugins modify the Sent folder when composing (#1486548)
- Added optional (max_recipients) support to restrict total number of recipients per message (#1484542)
- Re-organize editor buttons, add blockquote and search buttons
- Make possible to write inside or after a quoted html message (#1485476)
- Fix bugs on unexpected IMAP connection close (#1486190, #1486270)
- Iloha's imap.inc rewritten into rcube_imap_generic class
- Added contact groups in address book (not finished yet)
- Added PageUp/PageDown/Home/End keys support on lists (#1486430)
- Added possibility to select all messages in a folder (#1484756)
- Added 'imap_force_caps' option for after-login CAPABILITY checking (#1485750)
- Password: Support dovecotpw encryption
- TinyMCE 3.3.1
- Implemented messages copying using drag&drop + SHIFT (#1484086)
- Improved performance of folders operations (#1486525)
- Fix blocked.gif attachment is not attached to the message (#1486516)
- Managesieve: import from Horde-INGO
- Managesieve: support for more than one match (#1486078)
- Managesieve: support for selectively disabling rules within a single sieve script (#1485882)
- Threaded message listing now available
- Added sorting by ARRIVAL and CC
- Message list columns configurable by the user
- Removed 'index_sort' option, now we're using empty 'message_sort_col' for this
- virtuser_query: support other identity data (#1486148)
- Options virtuser_* replaced with virtuser_* plugins
- Plugin API: Implemented 'email2user' and 'user2email' hooks
- Fix forwarding message omits CC header (#1486305)
- Add 'default_charset' option to user preferences (#1485451)
- Add 'delete_always' option to user preferences
- Support/Require tls:// prefix in 'smtp_server' option for TLS connections
- Fix inconsistent behaviour of 'delete_always' option (#1486299)
- Fix deleting all messages from last list page (#1486293)
- Flag original messages when sending a draft (#1486203)
- Changed signature separator when top-posting (#1486330)
- Let the admin define defaults for search modifiers (#1485897)
- Fix long e-mail addresses validation (#1486453)
- Remember search modifiers in user prefs (#1486146)
- Added force_7bit option to force MIME encoding of plain/text messages (#1486510)
- Use case sensitive check when checking for default folders (#1486346)
- Fix checking for new mail: now checks unseen count of inbox (#1485794)
- Improve performance by avoiding unnecessary updates to the session table (#1486325)
- Fix invalid <font> tags which cause HTML message rendering problems (#1486521)
- Fix CVE-2010-0464: Disable DNS prefetching (#1486449)
- Fix Received headers to behave better with SpamAssassin (#1486513)
- Password: Make passwords encoding consistent with core, add 'password_charset' global option (#1486473)
- Fix adding contacts SQL error on mysql (#1486459)
- Squirrelmail_usercopy: support reply-to field (#1486506)
- Fix IE spellcheck suggestion popup issue (#1486471)
- Fix email address auto-completion shows regexp pattern (#1486258)
- Fix merging of configuration parameters: user prefs always survive (#1486368)
- Fix quota indicator value after folder purge/expunge (#1486488)
- Fix external mailto links support for use as protocol handler (#1486037)
- Fix attachment excessive memory use, support messages of any size (#1484660)
- Fix setting task name according to auth state
- Password: fix vpopmaild driver (#1486478)
- Add workaround for MySQL bug [http://bugs.mysql.com/bug.php?id=46293] (#1486474)
- Fix quoted text wrapping when replying to an HTML email in plain text (#1484141)
- Fix handling of extended mailto links (with params) (#1486354)
- Fix sorting by date of messages without date header on servers without SORT (#1486286)
- Fix inconsistency when not using default table names (#1486467)
- Fix folder rename/delete buttons do not appear on creation of first folder (#1486468)
- Fix character set conversion fails on systems where iconv doesn't accept //IGNORE (#1486375)
- Log in performance: Create default folders on first login only
- Import contacts into the selected address book (by Phil Weir)
- Add support for MDB2's 'sqlsrv' driver (#1486395)
- Use jQuery-1.4
- Removed problematic browser-caching of messages
- Fix incompatybility with suhosin.executor.disable_emodifier (#1486321)
- Use PLAIN auth when CRAM fails and imap_auth_type='check' (#1486371)
- Fix removal of <title> tag from HTML messages (#1486432)
- Fix 'force_https' to specified port when URL contains a port number (#1486411)
- Fix to-text converting of HTML entities inside b/strong/th/hX tags (#1486422)
- Bug in spellchecker suggestions when server charset != UTF8 (#1486406)
- Managesieve: Fix requires generation for multiple actions (#1486397)
- Fix LDAP problem with special characters in RDN (#1486320)
- Improved handling of message parts of type message/rfc822
- Plugin API: added 'quota' hook
- Fix parsing conditional comments in HTML messages (#1486350)
- Use built-in json_encode() for proper JSON format in AJAX replies
- Allow setting only selected params in 'message_compose' hook (#1486312)
- Plugin API: added 'message_compose_body' hook (#1486285)
- Fix counters of all folders are checked in 'getunread' action with check_all_folders disabled (#1486128)
- Fix displaying alternative parts in messages of type message/rfc822 (#1486246)
- Fix possible messages exposure when using Roundcube behind a proxy (#1486281)
- Fix unicode para and line separators in javascript response (#1486310)
- Additional_message_headers: allow unsetting headers, support plugin's config file (#1486268)
- Fix displaying of hidden directories in skins list (#1486301)
- Fix open_basedir restriction error when reading skins list (#1486304)
- Fix pasting from Office apps into html editor (#1486271)
- Fix empty <a> tags parsing (#1486272)
- Don't cut off attachment names when using non-RFC2231 encoding (#1485515)
- Allow inserting signatures above replied message body (#1484272)
- Managesieve 2.0: multi-script support
- Fix imap_auth_type regression (#1486263)
RELEASE 0.3.1
------------------
- Specify toolbar container in compose template (#1486247)
- Fix $_SERVER['HTTPS'] check for SSL forcing on IIS (#1486243)
- Avoid unnecessary page loads for selected tab (#1486032)
- Fix quota indicator issues by content generation on client-size (#1486197, #1486220)
- Don't display disabled sections in Settings (#1486099)
- Added server-side e-mail address validation with 'email_dns_check' option (#1485857)
- Fix login page loading into an iframe when session expires (#1485952)
- Allow setting port number in 'force_https' option (#1486091)
- Option 'force_https' replaced by 'force_https' plugin
- Fix IE issue with non-UTF-8 characters in AJAX response (#1486159)
- Partially fixed "empty body" issue by showing raw body of malformed message (#1486166)
- Fix importing/sending to email address with whitespace (#1486214)
- Added XIMSS (CommuniGate) driver for Password plugin
- Fix newly attached files are not saved in drafts w/o editing any text (#1486202)
- Added attachment upload indicator with parallel upload (#1486058)
- Use default_charset for bodies of messages without charset definition (#1486187)
- Password: added cPanel driver
- Fix return to first page from e-mail screen (#1486105)
- Fix handling HTML comments in HTML messages (#1486189)
- Fix folder/messagelist controls alignment - icons used (#1486072)
- Fix LDAP addressbook shows 'Contact not found' error sometimes (#1486178)
- Fix cache status checking + improve cache operations performance (#1486104)
- Prevent from setting INBOX as any of special folders (#1486114)
- Fix regular expression for e-mail address (#1486152)
- Fix Received header format
- Implemented sorting by message index - added 'index_sort' option (#1485936)
- Fix dl() use in installer (#1486150)
- Added 'ldap_debug' option
- Fix "Empty startup greeting" bug (#1486085)
- Fix setting user name in 'new_user_identity' plugin (#1486137)
- Fix incorrect count of new messages in folder list when using multiple IMAP clients (#1485995)
- Fix all folders checking for new messages with disabled caching (#1486128)
- Support skins in 'archive' and 'markasjunk' plugins
- Added 'html_editor' hook (#1486068)
- Fix DB constraint violation when populating messages cache (#1486052)
- Password: added password strength options (#1486062)
- Fix LDAP partial result warning (#1485536)
- Fix delete in message view deletes permanently with flag_for_deletion=true (#1486101)
- Use faster/secure mt_rand() (#1486094)
- Fix roundcube hangs on empty inbox with bincimapd (#1486093)
- Fix wrong headers for IE on servers without $_SERVER['HTTPS'] (#1485926)
- Force IE style headers for attachments in non-HTTPS session, 'use_https' option (#1485655)
- Check 'post_max_size' for upload max filesize (#1486089)
- Password Plugin: Fix %d inserts username instead of domain (#1486088)
- Fix rcube_mdb2::affected_rows() (#1486082)
RELEASE 0.3-stable
------------------
- Fix gn and givenName should be synonymous in LDAP addressbook (#1485892)
- Add mail_domain to LDAP email entries without @ sign (#1485201)
- Fix saving empty values in LDAP contact data (#1485781)
- Fix LDAP contact update when RDN field is changed (#1485788)
- Fix LDAP attributes case senitivity problems (#1485830)
- Fix LDAP addressbook browsing when only one directory is used (#1486022)
- Fix endless loop on error response for APPEND command (#1486060)
- Don't require date.timezone setting in installer (#1485989)
- Fix date sorting problem with Courier IMAP server (#1486065)
- Unselect pressed buttons on mouse up (#1485987)
- Don't set php_value error_log in .htaccess but mention in INSTALL (#1485924)
- Fix too small status/flag/attachment columns in Safari 4 (#1486063)
- Fix selection disabling while dragging splitter in webkit browsers (#1486056)
- Added 'new_messages' plugin hook (#1486005)
- Added 'logout_after' plugin hook (#1486042)
- Added 'message_compose' hook
- Added 'imap_connect' hook (#1485956)
- Fix vcard_attachments plugin (#1486035)
- Updated PEAR::Auth_SASL to 1.0.3 version
- Use sequence names only with PostgreSQL (#1486018)
- Re-designed User Preferences interface
- Fix MS SQL DDL (#1486020)
- Fix rcube_mdb2.php: call to setCharset not implemented in mssql driver (#1486019)
- Added 'display_next' option
- Fix rcube_mdb2::unixtimestamp for MS SQL (#1486015)
- Fix HTML washing to respect character encoding
- Fix endless loop in iil_C_Login() with Courier IMAP (#1486010)
- Fix #messagemenu display on IE (#1486006)
- Speedup UI by using sprites for (toolbar) buttons
- Fix charset names with X- prefix handling
- Fix displaying of HTML messages with unknown/malformed tags (#1486003)
RELEASE 0.3-RC1
---------------
- Fix import of vCard entries with params (#1485453)
- Fix HTML messages output with empty block elements (#1485974)
- Use request tokens to protect POST requests from CSRF
- Added hook when killing a session
- Added hook to write_log function (#1485971)
- Performance improvements by use UID commands (#1485690)
- Fix HTML editor tabIndex setting (#1485972)
- Added 'imap_debug' and 'smtp_debug' options
- Support strftime's format modifiers in date_* options (#1484806)
- Support %h variable in 'smtp_server' option (#1485766)
- Show SMTP errors in browser (#1485927)
- Allow WBR tag in HTML message (#1485960)
- Use spl_autoload_register() instead of __autoload (#1485947)
- Add hook for identities listing (#1485958)
- Trigger hook 'smtp_connect' when opening an SMTP connection (#1485954)
- Added config option to enforce HTTPS connections
- Fix non-unicode characters caching in unicode database (#1484608)
- Performance improvements of messages caching
- Fix empty Date header issue (#1485923)
- Open collapsed folders during drag & drop (#1485914)
- Fixed link text replacements (#1485789)
- Also trigger 'insertrow' events on page load (#1485826)
- No link on subject in IE browsers (#1484913)
- Fixed filename encoding according to RFC2231 (#1485875)
- Added message Edit feature (#1483891, #1484440)
- Fix message Etag generation for counter issues (#1485623)
- Fix messages searching on MailEnable IMAP (#1485762)
- Fixed many 'skip_deleted' issues (#1485634)
- Fixed messages list sorting on servers without SORT capability
- Colorized signatures in plain text messages
- Reviewed/fixed skip_deleted/read_when_deleted/flag_for_deletion options handling in UI
- Fix displaying of big maximum upload filesize (#1485889)
- Added possibility to invert messages selection
- After move/delete from 'show' action display next message instead of messages list (#1485887)
- Fixed problem with double quote at the end of folder name (#1485884)
- Speedup UI by using CSS sprites and etags/expires/deflate in Apache config (#1484858,#1485800)
- Support UID EXPUNGE: remove only moved/deleted messages
- Add drag cancelling with ESC key (#1484344)
- Support initial identity name from virtuser_query (#1484003)
- Added message menu, removed Print and Source buttons
- Added possibility to save message as .eml file (#1485861)
- Added 1 minute interval in autosave options (#1485854)
- Support UTF-7 encoding in messages (#1485832)
- Better support for malformed character names (#1485758)
RELEASE 0.3-BETA
----------------
- Plugin API + jQuery engine
- Added possibility to encrypt received header, option 'http_received_header_encrypt',
added some more logic in encrypt/decrypt functions for security
- Fix Answered/Forwarded flag setting for messages in subfolders
- Fix autocomplete problem with capital letters (#1485792)
- Support UUencode content encoding (#1485839)
- Minimize chance of race condition in session handling (#1485659, #1484678)
- Fix session handling on non-session SQL query error (#1485734)
- Fix html editor mode setting when reopening draft message (#1485834)
- Added quick search box menu (#1484304)
- Fix wrong column sort order icons (#1485823)
- Updated TinyMCE to 3.2.3 version
- Fix attachment names encoding when charset isn't specified in attachment part (#1484969)
- Fix message normal priority problem (#1485820)
- Fix autocomplete spinning wheel does not disappear (#1485804)
- Added log_date_format option (#1485709)
- Fix text wrapping in HTML editor after switching from plain text to HTML (#1485521)
- Fix auto-complete function hangs with plus sign (#1485815)
- Fix AJAX requests errors handler (#1485000)
- Speed up message list displaying on IE
- Fix read/write database recognition (#1485811)
RELEASE 0.2.2
-------------
- Fix quicksearchbox look in Chrome and Konqueror (#1484841)
- Fix UTF-8 byte-order mark removing (#1485514)
- Fix folders subscribtions on Konqueror (#1484841)
- Fix debug console on Konqueror and Safari
- Fix messagelist focus issue when modifying status of selected messages (#1485807)
- Support STARTTLS in IMAP connection (#1485284)
- Fix DEL key problem in search boxes (#1485528)
- Support several e-mail addresses per user from virtuser_file (#1485678)
- Fix drag&drop with scrolling on IE (#1485786)
- Fix adding signature separator in html mode (#1485350)
- Fix opening attachment marks message as read (#1485803)
- Fix 'temp_dir' does not support relative path under Windows (#1484529)
- Fix "Initialize Database" button missing from installer (#1485802)
- Fix compose window doesn't fit 1024x768 window (#1485396)
- Fix service not available error when pressing back from compose dialog (#1485552)
- Fix using mail() on Windows (#1485779)
- Fix word wrapping in message-part's <PRE>s for printing (#1485787)
- Fix incorrect word wrapping in outgoing plaintext multibyte messages (#1485714)
- Fix double footer in HTML message with embedded images
- Fix TNEF implementation bug (#1485773)
- Fix incorrect row id parsing for LDAP contacts list (#1485784)
- Fix 'mode' parameter in sqlite DSN (#1485772)
RELEASE 0.2.1
------------------
- Use US-ASCII as failover when Unicode searching fails (#1485762)
- Fix errors handling in IMAP command continuations (#1485762)
- Fix FETCH result parsing for servers returning flags at the end of result (#1485763)
- Fix datetime columns defaults in mysql's DDL (#1485641)
- Fix attaching more than nine inline images (#1485759)
- Support 'UNICODE-1-1-UTF-7' alias for UTF-7 encoding (#1485758)
- Fix mime-type detection using a hard-coded map (#1485311)
- Don't return empty string if charset conversion failed (#1485757)
- Disable concurrent autocomplete query results display (#1485743)
- Fix new lines stripped from message footer (#1485751)
- Fix IE problem with mouse click autocomplete (#1485739)
- Fix html body washing on reply/forward + fix attachments handling (#1485676)
- Fix multiple recipients input parsing (#1485733)
- Fix replying to message with html attachment (#1485676)
- Use default_charset for messages without specified charset (#1485661, #1484961)
- Support non-standard "GMT-XXXX" literal in date header (#1485729)
- Added TNEF support to decode MS Outlook attachments (winmail.dat)
- Fix "value continuation" MIME headers by adding required semicolon (#1485727)
- Fix pressing select all/unread multiple times (#1485723)
- Fix selecting all unread does not honor new messages (#1485724)
- Fix some base64 encoded attachments handling (#1485725)
- Support NGINX as IMAP backend: better BAD response handling (#1485720)
- Performance fix: don't fetch attachment parts headers twice to parse filename
- Fix checking for recent messages on various IMAP servers (#1485702)
- Performance fix: Don't fetch quota and recent messages in "message view" mode
- Fix displaying of alternative-inside-alternative messages (#1485713)
- Fix MDNSent flag checking, use arbitrary keywords (asterisk) flag (#1485706)
- Fix creation of folders with '&' sign in name
- Fix parsing of email addresses without angle brackets (#1485693)
- Save spellcheck corrections when switching from plain to html editor (and spellchecking is on)
- Fix large search results on server without SORT capability (#1485668)
- Get rid of preg_replace() with eval modifier and create_function usage (#1485686)
- Bring back <base> and <link> tags in HTML messages
- Fix XSS vulnerability through background attributes as reported by Julien Cayssol
- Fix problems with backslash as IMAP hierarchy delimiter (#1484467)
- Secure vcard export by getting rid of preg's 'e' modifier use (#1485689)
- Fix authentication when submitting form with existing session (#1485679)
- Allow absolute URLs to images in HTML messages/sigs (#1485666)
- Fix message body which contains both inline attachments and emotions
- Fix SQL query execution errors handling in rcube_mdb2 class (#1485509)
- Fix address names with '@' sign handling (#1485654)
- Improve messages display performance
- Fix messages searching with 'to:' modifier
RELEASE 0.2-STABLE
------------------
- 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)
- imap.inc: Fixed iil_C_FetchStructureString() to handle many
literal strings in response (#1484969)
- Support for subfolders in default/protected folders (#1484665)
- Disallowed delimiter in folder name (#1484803)
- Support " and \ in folder names
- Escape \ in login (#1484614)
- Better HTML sanitization with the DOM-based washtml script (#1484701)
- Fixed sorting of folders with non-ascii characters
- Fixed Mysql DDL for default identities creation (#1485070)
- In Preferences added possibility to configure 'read_when_deleted',
'mdn_requests', 'flag_for_deletion' options
- Made IMAP auth type configurable (#1483825)
- Fixed empty values with FROM_UNIXTIME() in rcube_mdb2 (#1485055)
- Fixed attachment list on IE 6/7 (#1484807)
- Fixed JavaScript in compose.html that shows cc/bcc fields if populated
- Make password input fields of type password in installer (#1484886)
- Don't attempt to delete cache entries if enable_caching is FALSE (#1485051)
- Optimized messages sorting on servers without sort capability (#1485049)
- Corrected message headers decoding when charset isn't specified and improved
support for native languages (#1485050, #1485048)
- Expanded LDAP configuration options to support LDAP server writes.
- Installer: encode special characters in DB username/password (#1485042)
- Fixed management of folders with national characters in names (#1485036, #1485001)
- Fixed identities saving when using MDB2 pgsql driver (#1485032)
- Fixed BCC header reset (#1484997)
- Improved messages list performance - patch from Justin Heesemann
- Append skin_path to images location only when it starts with '/' sign (#1484859)
- Fix IMAP response in message body when message has no body (#1484964)
- Fixed non-RFC dates formatting (#1484901)
- Fixed typo in set_charset() (#1484991)
- Decode entities when inserting HTML signature to plain text message (#1484990)
- HTML editing is now working with PHP5 updates and TinyMCE v3.0.6
- Fixed signature loading on Windows (#1484545)
- Added language support to HTML editing (#1484862)
- Fixed remove signature when replying (#1333167)
- Fixed problem with line with a space at the end (#1484916)
- Fixed <!DOCTYPE> tag filtering (#1484391)
- Fixed <?xml> tag filtering (#1484403)
- Added sections (fieldset+label) in Settings interface
- Mark as read in one action with message preview (#1484972)
- Deleted redundant quota reads (#1484972)
- Added options for empty trash and expunge inbox on logout (#1483863)
- 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 <? in text/html message parts
- Hide border around radio buttons
- Applied patch for attachment download by crichardson (closes #1484198)
- Fixed bug in Postgres DB handling (closes #1484068)
- Fixed bug of invalid calls to fetchRow() in rcube_db.inc (closes #1484280)
- Fixed array_merge bug (closes #1484281)
- Fixed flag for deletion in list view (closes #1484264)
- Finally support semicolons as recipient separator (closes ##1484251)
- Fixed message headers (subject) encoding
- check if safe mode is on or not (closes #1484269)
- Show "no subject" in message list if subject is missing (closes #1484243)
- Solved page caching of message preview (closes #1484153)
- Only use gzip compression if configured (closes #1484236)
- Fixed priority selector issue (#1484150)
- Fixed some CSS issues in default skin (closes #1484210 and #1484161)
- Prevent from double quoting of numeric HTML character references (closes #1484253)
- Fixed display of HTML message attachments (closes #1484178)
- Applied patch for preview caching (closes #1484186)
- Added error handling for attachment uploads
- Use multibyte safe string functions where necessary (closes #1483988)
- Applied security patch to validate the submitted host value (by Kees Cook)
- Applied security patch to validate input values when deleting contacts (by Kees Cook)
- Applied security patch that sanitizes emoticon paths when attaching them (by Kees Cook)
- Applied a patch to more aggressively sanitize a HTML message
- Visualize blocked images in HTML messages
- Fixed wrong message listing when showing search results (closes #1484131)
- Show remote images when opening HTML message part as attachment
- Improve memory usage when sending mail (closes #1484098)
- Mark messages as read once the preview is loaded (closes #1484132)
- Include smtp final response in log (closes #1484081)
- Corrected date string in sent message header (closes #1484125)
- Correclty choose "To" column in sent and draft mailboxes (closes #1483943)
- Changed srong tooltips for message browse buttons (closes #1483930)
- Fixed signature delimeter character to be standard (Bug #1484035)
- Fixed XSS vulnerability (Bug #1484109)
- Remove newlines from mail headers (Bug #1484031)
- Selection issues when moving/deleting (Bug #1484044)
- Applied patch of Clement Moulin for imap host auto-selection
- ISO-encode IMAP password for plaintext login (Bugs #1483977 & #1483886)
- Fixed folder name encoding in subscription list (Bug #1484113)
- Fixed JS errors in identity list (Bug #1484120)
- Translate foldernames in folder form (closes #1484113)
- Added first and last buttons to message list, address book
and message detail
- Pressing Shift-Del bypasses Trash folder
- Enable purge command for Junk folder
- Fetch all aliases if virtuser_query is used instead
- Re-enabled multi select of contacts (Bug #1484017)
- Enable contact editing right after creation (Bug #1459641)
- Correct UTF-7 to UTF-8 conversion if mbstring is not available
- Fixed IMAP fetch of message body (Bug #1484019)
- Fixed safe_mode problems (Bug #1418381)
- Fixed wrong header encoding (Bug #1483976)
- Made automatic draft saving configurable
- Fixed JS bug when renaming folders (Bug #1483989)
- Added quota display as image (by Brett Patterson)
- Corrected creation of a message-id
- New indentation for quoted message text
- Improved HTML validity
- Fixed URL character set (Ticket #1445501)
- Fixed saving of contact into MySQL from LDAP query results (Ticket #1483820)
- Fixed folder renaming: unsubscribe before rename (Bug #1483920)
- Finalized new message parsing (+ chaching)
- Fixed wrong usage of mbstring (Bug #1462439)
- Set default spelling language (Ticket #1483938)
- Added support for Nox Spell Server
- Re-built message parsing (Bug #1327068)
Now based on the message structure delivered by the IMAP server.
- Fixed some XSS and SQL injection issues
- Fixed charset problems with folder renaming
diff --git a/plugins/hide_blockquote/hide_blockquote.php b/plugins/hide_blockquote/hide_blockquote.php
index ca0273a5d..7af163dcd 100644
--- a/plugins/hide_blockquote/hide_blockquote.php
+++ b/plugins/hide_blockquote/hide_blockquote.php
@@ -1,82 +1,78 @@
<?php
/**
* Quotation block hidding
*
* Plugin that adds a possibility to hide long blocks of cited text in messages.
*
* Configuration:
* // Minimum number of citation lines. Longer citation blocks will be hidden.
* // 0 - no limit (no hidding).
* $rcmail_config['hide_blockquote_limit'] = 0;
*
* @version @package_version@
* @license GNU GPLv3+
* @author Aleksander Machniak <alec@alec.pl>
*/
class hide_blockquote extends rcube_plugin
{
public $task = 'mail|settings';
function init()
{
$rcmail = rcmail::get_instance();
if ($rcmail->task == 'mail'
&& ($rcmail->action == 'preview' || $rcmail->action == 'show')
&& ($limit = $rcmail->config->get('hide_blockquote_limit'))
) {
// include styles
- $skin = $rcmail->config->get('skin');
- if (!file_exists($this->home."/skins/$skin/style.css")) {
- $skin = 'default';
- }
- $this->include_stylesheet("skins/$skin/style.css");
+ $this->include_stylesheet($this->local_skin_path() . "/style.css");
// Script and localization
$this->include_script('hide_blockquote.js');
$this->add_texts('localization', true);
// set env variable for client
$rcmail->output->set_env('blockquote_limit', $limit);
}
else if ($rcmail->task == 'settings') {
$dont_override = $rcmail->config->get('dont_override', array());
if (!in_array('hide_blockquote_limit', $dont_override)) {
$this->add_hook('preferences_list', array($this, 'prefs_table'));
$this->add_hook('preferences_save', array($this, 'save_prefs'));
}
}
}
function prefs_table($args)
{
if ($args['section'] != 'mailview') {
return $args;
}
$this->add_texts('localization');
$rcmail = rcmail::get_instance();
$limit = (int) $rcmail->config->get('hide_blockquote_limit');
$field_id = 'hide_blockquote_limit';
$input = new html_inputfield(array('name' => '_'.$field_id, 'id' => $field_id, 'size' => 5));
$args['blocks']['main']['options']['hide_blockquote_limit'] = array(
'title' => $this->gettext('quotelimit'),
'content' => $input->show($limit ? $limit : '')
);
return $args;
}
function save_prefs($args)
{
if ($args['section'] == 'mailview') {
$args['prefs']['hide_blockquote_limit'] = (int) get_input_value('_hide_blockquote_limit', RCUBE_INPUT_POST);
}
return $args;
}
}
diff --git a/plugins/hide_blockquote/skins/default/style.css b/plugins/hide_blockquote/skins/larry/style.css
similarity index 100%
rename from plugins/hide_blockquote/skins/default/style.css
rename to plugins/hide_blockquote/skins/larry/style.css
diff --git a/program/include/html.php b/program/include/html.php
index d15d50875..c6507f813 100644
--- a/program/include/html.php
+++ b/program/include/html.php
@@ -1,857 +1,857 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/html.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2011, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Helper class to create valid XHTML code |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Class for HTML code creation
*
* @package HTML
*/
class html
{
protected $tagname;
protected $attrib = array();
protected $allowed = array();
protected $content;
public static $doctype = 'xhtml';
public static $lc_tags = true;
public static $common_attrib = array('id','class','style','title','align');
public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script');
/**
* Constructor
*
* @param array $attrib Hash array with tag attributes
*/
public function __construct($attrib = array())
{
if (is_array($attrib)) {
$this->attrib = $attrib;
}
}
/**
* Return the tag code
*
* @return string The finally composed HTML tag
*/
public function show()
{
return self::tag($this->tagname, $this->attrib, $this->content, array_merge(self::$common_attrib, $this->allowed));
}
/****** STATIC METHODS *******/
/**
* Generic method to create a HTML tag
*
* @param string $tagname Tag name
* @param array $attrib Tag attributes as key/value pairs
* @param string $content Optinal Tag content (creates a container tag)
* @param array $allowed_attrib List with allowed attributes, omit to allow all
* @return string The XHTML tag
*/
public static function tag($tagname, $attrib = array(), $content = null, $allowed_attrib = null)
{
if (is_string($attrib))
$attrib = array('class' => $attrib);
$inline_tags = array('a','span','img');
$suffix = $attrib['nl'] || ($content && $attrib['nl'] !== false && !in_array($tagname, $inline_tags)) ? "\n" : '';
$tagname = self::$lc_tags ? strtolower($tagname) : $tagname;
if (isset($content) || in_array($tagname, self::$containers)) {
$suffix = $attrib['noclose'] ? $suffix : '</' . $tagname . '>' . $suffix;
unset($attrib['noclose'], $attrib['nl']);
return '<' . $tagname . self::attrib_string($attrib, $allowed_attrib) . '>' . $content . $suffix;
}
else {
return '<' . $tagname . self::attrib_string($attrib, $allowed_attrib) . '>' . $suffix;
}
}
/**
*
*/
public static function doctype($type)
{
$doctypes = array(
'html5' => '<!DOCTYPE html>',
'xhtml' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
);
if ($doctypes[$type]) {
self::$doctype = preg_replace('/-\w+$/', '', $type);
return $doctypes[$type];
}
return '';
}
/**
* Derrived method for <div> containers
*
* @param mixed $attr Hash array with tag attributes or string with class name
* @param string $cont Div content
* @return string HTML code
* @see html::tag()
*/
public static function div($attr = null, $cont = null)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
return self::tag('div', $attr, $cont, array_merge(self::$common_attrib, array('onclick')));
}
/**
* Derrived method for <p> blocks
*
* @param mixed $attr Hash array with tag attributes or string with class name
* @param string $cont Paragraph content
* @return string HTML code
* @see html::tag()
*/
public static function p($attr = null, $cont = null)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
return self::tag('p', $attr, $cont, self::$common_attrib);
}
/**
* Derrived method to create <img />
*
* @param mixed $attr Hash array with tag attributes or string with image source (src)
* @return string HTML code
* @see html::tag()
*/
public static function img($attr = null)
{
if (is_string($attr)) {
$attr = array('src' => $attr);
}
return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib,
- array('src','alt','width','height','border','usemap','onclick')));
+ array('src','alt','width','height','border','usemap','onclick')));
}
/**
* Derrived method for link tags
*
* @param mixed $attr Hash array with tag attributes or string with link location (href)
* @param string $cont Link content
* @return string HTML code
* @see html::tag()
*/
public static function a($attr, $cont)
{
if (is_string($attr)) {
$attr = array('href' => $attr);
}
return self::tag('a', $attr, $cont, array_merge(self::$common_attrib,
array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup')));
}
/**
* Derrived method for inline span tags
*
* @param mixed $attr Hash array with tag attributes or string with class name
* @param string $cont Tag content
* @return string HTML code
* @see html::tag()
*/
public static function span($attr, $cont)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
return self::tag('span', $attr, $cont, self::$common_attrib);
}
/**
* Derrived method for form element labels
*
* @param mixed $attr Hash array with tag attributes or string with 'for' attrib
* @param string $cont Tag content
* @return string HTML code
* @see html::tag()
*/
public static function label($attr, $cont)
{
if (is_string($attr)) {
$attr = array('for' => $attr);
}
return self::tag('label', $attr, $cont, array_merge(self::$common_attrib, array('for')));
}
/**
* Derrived method to create <iframe></iframe>
*
* @param mixed $attr 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 to create <script> tags
*
* @param mixed $attr Hash array with tag attributes or string with script source (src)
* @param string $cont Javascript code to be placed as tag content
* @return string HTML code
* @see html::tag()
*/
public static function script($attr, $cont = null)
{
if (is_string($attr)) {
$attr = array('src' => $attr);
}
if ($cont) {
if (self::$doctype == 'xhtml')
$cont = "\n/* <![CDATA[ */\n" . $cont . "\n/* ]]> */\n";
else
$cont = "\n" . $cont . "\n";
}
return self::tag('script', $attr + array('type' => 'text/javascript', 'nl' => true),
$cont, array_merge(self::$common_attrib, array('src','type','charset')));
}
/**
* Derrived method for line breaks
*
* @return string HTML code
* @see html::tag()
*/
public static function br()
{
return self::tag('br');
}
/**
* Create string with attributes
*
* @param array $attrib Associative arry with tag attributes
* @param array $allowed List of allowed attributes
* @return string Valid attribute string
*/
public static function attrib_string($attrib = array(), $allowed = null)
{
if (empty($attrib)) {
return '';
}
$allowed_f = array_flip((array)$allowed);
$attrib_arr = array();
foreach ($attrib as $key => $value) {
// skip size if not numeric
if ($key == 'size' && !is_numeric($value)) {
continue;
}
// ignore "internal" or not allowed attributes
if ($key == 'nl' || ($allowed && !isset($allowed_f[$key])) || $value === null) {
continue;
}
// skip empty eventhandlers
if (preg_match('/^on[a-z]+/', $key) && !$value) {
continue;
}
// attributes with no value
if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) {
if ($value) {
$attrib_arr[] = $key . '="' . $key . '"';
}
}
else {
$attrib_arr[] = $key . '="' . self::quote($value, true) . '"';
}
}
return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
}
/**
* Convert a HTML attribute string attributes to an associative array (name => value)
*
* @param string Input string
* @return array Key-value pairs of parsed attributes
*/
public static function parse_attrib_string($str)
{
$attrib = array();
$regexp = '/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui';
preg_match_all($regexp, stripslashes($str), $regs, PREG_SET_ORDER);
// convert attributes to an associative array (name => value)
if ($regs) {
foreach ($regs as $attr) {
$attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]);
}
}
return $attrib;
}
/**
* Replacing specials characters in html attribute value
*
* @param string $str Input string
* @param bool $validate Enables double quotation prevention
*
* @return string The quoted string
*/
public static function quote($str, $validate = false)
{
$str = htmlspecialchars($str, ENT_COMPAT, RCMAIL_CHARSET);
// avoid douple quotation of &
// @TODO: get rid of it
if ($validate) {
$str = preg_replace('/&amp;([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $str);
}
return $str;
}
}
/**
* Class to create an HTML input field
*
* @package HTML
*/
class html_inputfield extends html
{
protected $tagname = 'input';
protected $type = 'text';
protected $allowed = array(
'type','name','value','size','tabindex','autocapitalize',
'autocomplete','checked','onchange','onclick','disabled','readonly',
'spellcheck','results','maxlength','src','multiple','placeholder',
);
/**
* Object constructor
*
* @param array $attrib Associative array with tag attributes
*/
public function __construct($attrib = array())
{
if (is_array($attrib)) {
$this->attrib = $attrib;
}
if ($attrib['type']) {
$this->type = $attrib['type'];
}
}
/**
* Compose input tag
*
* @param string $value Field value
* @param array $attrib Additional attributes to override
* @return string HTML output
*/
public function show($value = null, $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// set value attribute
if ($value !== null) {
$this->attrib['value'] = $value;
}
// set type
$this->attrib['type'] = $this->type;
return parent::show();
}
}
/**
* Class to create an HTML password field
*
* @package HTML
*/
class html_passwordfield extends html_inputfield
{
protected $type = 'password';
}
/**
* Class to create an hidden HTML input field
*
* @package HTML
*/
class html_hiddenfield extends html
{
protected $tagname = 'input';
protected $type = 'hidden';
protected $fields_arr = array();
protected $allowed = array('type','name','value','onchange','disabled','readonly');
/**
* Constructor
*
* @param array $attrib Named tag attributes
*/
public function __construct($attrib = null)
{
if (is_array($attrib)) {
$this->add($attrib);
}
}
/**
* Add a hidden field to this instance
*
* @param array $attrib Named tag attributes
*/
public function add($attrib)
{
$this->fields_arr[] = $attrib;
}
/**
* Create HTML code for the hidden fields
*
* @return string Final HTML code
*/
public function show()
{
$out = '';
foreach ($this->fields_arr as $attrib) {
$out .= self::tag($this->tagname, array('type' => $this->type) + $attrib);
}
return $out;
}
}
/**
* Class to create HTML radio buttons
*
* @package HTML
*/
class html_radiobutton extends html_inputfield
{
protected $type = 'radio';
/**
* Get HTML code for this object
*
* @param string $value Value of the checked field
* @param array $attrib Additional attributes to override
* @return string HTML output
*/
public function show($value = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// set value attribute
$this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
return parent::show();
}
}
/**
* Class to create HTML checkboxes
*
* @package HTML
*/
class html_checkbox extends html_inputfield
{
protected $type = 'checkbox';
/**
* Get HTML code for this object
*
* @param string $value Value of the checked field
* @param array $attrib Additional attributes to override
* @return string HTML output
*/
public function show($value = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// set value attribute
$this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
return parent::show();
}
}
/**
* Class to create an HTML textarea
*
* @package HTML
*/
class html_textarea extends html
{
protected $tagname = 'textarea';
protected $allowed = array('name','rows','cols','wrap','tabindex',
'onchange','disabled','readonly','spellcheck');
/**
* Get HTML code for this object
*
* @param string $value Textbox value
* @param array $attrib Additional attributes to override
* @return string HTML output
*/
public function show($value = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// take value attribute as content
if (empty($value) && !empty($this->attrib['value'])) {
$value = $this->attrib['value'];
}
// make shure we don't print the value attribute
if (isset($this->attrib['value'])) {
unset($this->attrib['value']);
}
if (!empty($value) && empty($this->attrib['is_escaped'])) {
$value = self::quote($value, true);
}
return self::tag($this->tagname, $this->attrib, $value,
array_merge(self::$common_attrib, $this->allowed));
}
}
/**
* Builder for HTML drop-down menus
* Syntax:<pre>
* // create instance. arguments are used to set attributes of select-tag
* $select = new html_select(array('name' => 'fieldname'));
*
* // add one option
* $select->add('Switzerland', 'CH');
*
* // add multiple options
* $select->add(array('Switzerland','Germany'), array('CH','DE'));
*
* // generate pulldown with selection 'Switzerland' and return html-code
* // as second argument the same attributes available to instanciate can be used
* print $select->show('CH');
* </pre>
*
* @package HTML
*/
class html_select extends html
{
protected $tagname = 'select';
protected $options = array();
protected $allowed = array('name','size','tabindex','autocomplete',
'multiple','onchange','disabled','rel');
/**
* Add a new option to this drop-down
*
* @param mixed $names Option name or array with option names
* @param mixed $values Option value or array with option values
*/
public function add($names, $values = null)
{
if (is_array($names)) {
foreach ($names as $i => $text) {
$this->options[] = array('text' => $text, 'value' => $values[$i]);
}
}
else {
$this->options[] = array('text' => $names, 'value' => $values);
}
}
/**
* Get HTML code for this object
*
* @param string $select Value of the selection option
* @param array $attrib Additional attributes to override
* @return string HTML output
*/
public function show($select = array(), $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
$this->content = "\n";
$select = (array)$select;
foreach ($this->options as $option) {
$attr = array(
'value' => $option['value'],
'selected' => (in_array($option['value'], $select, true) ||
in_array($option['text'], $select, true)) ? 1 : null);
$option_content = $option['text'];
if (empty($this->attrib['is_escaped'])) {
$option_content = self::quote($option_content, true);
}
$this->content .= self::tag('option', $attr, $option_content);
}
return parent::show();
}
}
/**
* Class to build an HTML table
*
* @package HTML
*/
class html_table extends html
{
protected $tagname = 'table';
protected $allowed = array('id','class','style','width','summary',
'cellpadding','cellspacing','border');
private $header = array();
private $rows = array();
private $rowindex = 0;
private $colindex = 0;
/**
* Constructor
*
* @param array $attrib Named tag attributes
*/
public function __construct($attrib = array())
{
$default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => 0) : array();
$this->attrib = array_merge($attrib, $default_attrib);
}
/**
* Add a table cell
*
* @param array $attr Cell attributes
* @param string $cont Cell content
*/
public function add($attr, $cont)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
$cell = new stdClass;
$cell->attrib = $attr;
$cell->content = $cont;
$this->rows[$this->rowindex]->cells[$this->colindex] = $cell;
$this->colindex++;
if ($this->attrib['cols'] && $this->colindex == $this->attrib['cols']) {
$this->add_row();
}
}
/**
* Add a table header cell
*
* @param array $attr Cell attributes
* @param string $cont Cell content
*/
public function add_header($attr, $cont)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
$cell = new stdClass;
$cell->attrib = $attr;
$cell->content = $cont;
$this->header[] = $cell;
}
/**
* Remove a column from a table
* Useful for plugins making alterations
*
* @param string $class
*/
public function remove_column($class)
{
// Remove the header
foreach ($this->header as $index=>$header){
if ($header->attrib['class'] == $class){
unset($this->header[$index]);
break;
}
}
// Remove cells from rows
foreach ($this->rows as $i=>$row){
foreach ($row->cells as $j=>$cell){
if ($cell->attrib['class'] == $class){
unset($this->rows[$i]->cells[$j]);
break;
}
}
}
}
/**
* Jump to next row
*
* @param array $attr Row attributes
*/
public function add_row($attr = array())
{
$this->rowindex++;
$this->colindex = 0;
$this->rows[$this->rowindex] = new stdClass;
$this->rows[$this->rowindex]->attrib = $attr;
$this->rows[$this->rowindex]->cells = array();
}
/**
* Set row attributes
*
* @param array $attr Row attributes
* @param int $index Optional row index (default current row index)
*/
public function set_row_attribs($attr = array(), $index = null)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
if ($index === null) {
$index = $this->rowindex;
}
$this->rows[$index]->attrib = $attr;
}
/**
* Get row attributes
*
* @param int $index Row index
*
* @return array Row attributes
*/
public function get_row_attribs($index = null)
{
if ($index === null) {
$index = $this->rowindex;
}
return $this->rows[$index] ? $this->rows[$index]->attrib : null;
}
/**
* Build HTML output of the table data
*
* @param array $attrib Table attributes
* @return string The final table HTML code
*/
public function show($attrib = null)
{
if (is_array($attrib))
$this->attrib = array_merge($this->attrib, $attrib);
$thead = $tbody = "";
// include <thead>
if (!empty($this->header)) {
$rowcontent = '';
foreach ($this->header as $c => $col) {
$rowcontent .= self::tag('td', $col->attrib, $col->content);
}
$thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib));
}
foreach ($this->rows as $r => $row) {
$rowcontent = '';
foreach ($row->cells as $c => $col) {
$rowcontent .= self::tag('td', $col->attrib, $col->content);
}
if ($r < $this->rowindex || count($row->cells)) {
$tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib);
}
}
if ($this->attrib['rowsonly']) {
return $tbody;
}
// add <tbody>
$this->content = $thead . self::tag('tbody', null, $tbody);
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);
}
/**
* Remove table body (all rows)
*/
public function remove_body()
{
$this->rows = array();
$this->rowindex = 0;
}
}
diff --git a/program/include/rcube.php b/program/include/rcube.php
index 494b5c3dd..84014ef5c 100644
--- a/program/include/rcube.php
+++ b/program/include/rcube.php
@@ -1,1202 +1,1246 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/rcube.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2008-2012, The Roundcube Dev Team |
| Copyright (C) 2011-2012, Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Framework base class providing core functions and holding |
| instances of all 'global' objects like db- and storage-connections |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Base class of the Roundcube Framework
* implemented as singleton
*
- * @package Core
+ * @package Framework
+ * @subpackage Core
*/
class rcube
{
- const INIT_WITH_DB = 1;
- const INIT_WITH_PLUGINS = 2;
-
- /**
- * Singleton instace of rcube
- *
- * @var rcmail
- */
- static protected $instance;
-
- /**
- * Stores instance of rcube_config.
- *
- * @var rcube_config
- */
- public $config;
-
- /**
- * Instace of database class.
- *
- * @var rcube_pdo
- */
- public $db;
-
- /**
- * Instace of Memcache class.
- *
- * @var Memcache
- */
- public $memcache;
-
- /**
- * Instace of rcube_session class.
- *
- * @var rcube_session
- */
- public $session;
-
- /**
- * Instance of rcube_smtp class.
- *
- * @var rcube_smtp
- */
- public $smtp;
-
- /**
- * Instance of rcube_storage class.
- *
- * @var rcube_storage
- */
- public $storage;
-
- /**
- * Instance of rcube_output class.
- *
- * @var rcube_output
- */
- public $output;
-
- /**
- * Instance of rcube_plugin_api.
- *
- * @var rcube_plugin_api
- */
- public $plugins;
-
-
- /* private/protected vars */
- protected $texts;
- protected $caches = array();
- protected $shutdown_functions = array();
- protected $expunge_cache = false;
-
-
- /**
- * This implements the 'singleton' design pattern
- *
- * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants
- * @return rcube The one and only instance
- */
- static function get_instance($mode = 0)
- {
- if (!self::$instance) {
- self::$instance = new rcube();
- self::$instance->init($mode);
- }
+ const INIT_WITH_DB = 1;
+ const INIT_WITH_PLUGINS = 2;
- return self::$instance;
- }
-
-
- /**
- * Private constructor
- */
- protected function __construct()
- {
- // load configuration
- $this->config = new rcube_config();
- $this->plugins = new rcube_dummy_plugin_api;
-
- register_shutdown_function(array($this, 'shutdown'));
- }
-
-
- /**
- * Initial startup function
- */
- protected function init($mode = 0)
- {
- // initialize syslog
- if ($this->config->get('log_driver') == 'syslog') {
- $syslog_id = $this->config->get('syslog_id', 'roundcube');
- $syslog_facility = $this->config->get('syslog_facility', LOG_USER);
- openlog($syslog_id, LOG_ODELAY, $syslog_facility);
- }
+ /**
+ * Singleton instace of rcube
+ *
+ * @var rcmail
+ */
+ static protected $instance;
- // connect to database
- if ($mode & self::INIT_WITH_DB) {
- $this->get_dbh();
- }
+ /**
+ * Stores instance of rcube_config.
+ *
+ * @var rcube_config
+ */
+ public $config;
+
+ /**
+ * Instace of database class.
+ *
+ * @var rcube_db
+ */
+ public $db;
+
+ /**
+ * Instace of Memcache class.
+ *
+ * @var Memcache
+ */
+ public $memcache;
+
+ /**
+ * Instace of rcube_session class.
+ *
+ * @var rcube_session
+ */
+ public $session;
+
+ /**
+ * Instance of rcube_smtp class.
+ *
+ * @var rcube_smtp
+ */
+ public $smtp;
+
+ /**
+ * Instance of rcube_storage class.
+ *
+ * @var rcube_storage
+ */
+ public $storage;
+
+ /**
+ * Instance of rcube_output class.
+ *
+ * @var rcube_output
+ */
+ public $output;
+
+ /**
+ * Instance of rcube_plugin_api.
+ *
+ * @var rcube_plugin_api
+ */
+ public $plugins;
- // create plugin API and load plugins
- if ($mode & self::INIT_WITH_PLUGINS) {
- $this->plugins = rcube_plugin_api::get_instance();
+
+ /* private/protected vars */
+ protected $texts;
+ protected $caches = array();
+ protected $shutdown_functions = array();
+ protected $expunge_cache = false;
+
+
+ /**
+ * This implements the 'singleton' design pattern
+ *
+ * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants
+ *
+ * @return rcube The one and only instance
+ */
+ static function get_instance($mode = 0)
+ {
+ if (!self::$instance) {
+ self::$instance = new rcube();
+ self::$instance->init($mode);
+ }
+
+ return self::$instance;
}
- }
-
-
- /**
- * Get the current database connection
- *
- * @return rcube_pdo Database connection object
- */
- public function get_dbh()
- {
- if (!$this->db) {
- $config_all = $this->config->all();
- $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
- $this->db->set_debug((bool)$config_all['sql_debug']);
+
+
+ /**
+ * Private constructor
+ */
+ protected function __construct()
+ {
+ // load configuration
+ $this->config = new rcube_config;
+ $this->plugins = new rcube_dummy_plugin_api;
+
+ register_shutdown_function(array($this, 'shutdown'));
}
- return $this->db;
- }
-
-
- /**
- * Get global handle for memcache access
- *
- * @return object Memcache
- */
- public function get_memcache()
- {
- if (!isset($this->memcache)) {
- // no memcache support in PHP
- if (!class_exists('Memcache')) {
- $this->memcache = false;
- return false;
- }
- $this->memcache = new Memcache;
- $this->mc_available = 0;
+ /**
+ * Initial startup function
+ */
+ protected function init($mode = 0)
+ {
+ // initialize syslog
+ if ($this->config->get('log_driver') == 'syslog') {
+ $syslog_id = $this->config->get('syslog_id', 'roundcube');
+ $syslog_facility = $this->config->get('syslog_facility', LOG_USER);
+ openlog($syslog_id, LOG_ODELAY, $syslog_facility);
+ }
- // add all configured hosts to pool
- $pconnect = $this->config->get('memcache_pconnect', true);
- foreach ($this->config->get('memcache_hosts', array()) as $host) {
- if (substr($host, 0, 7) != 'unix://') {
- list($host, $port) = explode(':', $host);
- if (!$port) $port = 11211;
+ // connect to database
+ if ($mode & self::INIT_WITH_DB) {
+ $this->get_dbh();
}
- else {
- $port = 0;
+
+ // create plugin API and load plugins
+ if ($mode & self::INIT_WITH_PLUGINS) {
+ $this->plugins = rcube_plugin_api::get_instance();
}
+ }
- $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
- }
- // test connection and failover (will result in $this->mc_available == 0 on complete failure)
- $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
+ /**
+ * Get the current database connection
+ *
+ * @return rcube_db Database object
+ */
+ public function get_dbh()
+ {
+ if (!$this->db) {
+ $config_all = $this->config->all();
+ $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
+ $this->db->set_debug((bool)$config_all['sql_debug']);
+ }
- if (!$this->mc_available)
- $this->memcache = false;
+ return $this->db;
}
- return $this->memcache;
- }
+ /**
+ * Get global handle for memcache access
+ *
+ * @return object Memcache
+ */
+ public function get_memcache()
+ {
+ if (!isset($this->memcache)) {
+ // no memcache support in PHP
+ if (!class_exists('Memcache')) {
+ $this->memcache = false;
+ return false;
+ }
- /**
- * Callback for memcache failure
- */
- public function memcache_failure($host, $port)
- {
- static $seen = array();
+ $this->memcache = new Memcache;
+ $this->mc_available = 0;
- // only report once
- if (!$seen["$host:$port"]++) {
- $this->mc_available--;
- self::raise_error(array('code' => 604, 'type' => 'db',
- 'line' => __LINE__, 'file' => __FILE__,
- 'message' => "Memcache failure on host $host:$port"),
- true, false);
- }
- }
-
-
- /**
- * Initialize and get cache object
- *
- * @param string $name Cache identifier
- * @param string $type Cache type ('db', 'apc' or 'memcache')
- * @param string $ttl Expiration time for cache items
- * @param bool $packed Enables/disables data serialization
- *
- * @return rcube_cache Cache object
- */
- public function get_cache($name, $type='db', $ttl=0, $packed=true)
- {
- if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) {
- $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed);
- }
+ // add all configured hosts to pool
+ $pconnect = $this->config->get('memcache_pconnect', true);
+ foreach ($this->config->get('memcache_hosts', array()) as $host) {
+ if (substr($host, 0, 7) != 'unix://') {
+ list($host, $port) = explode(':', $host);
+ if (!$port) $port = 11211;
+ }
+ else {
+ $port = 0;
+ }
- return $this->caches[$name];
- }
-
-
- /**
- * Create SMTP object and connect to server
- *
- * @param boolean True if connection should be established
- */
- public function smtp_init($connect = false)
- {
- $this->smtp = new rcube_smtp();
-
- if ($connect)
- $this->smtp->connect();
- }
-
-
- /**
- * Initialize and get storage object
- *
- * @return rcube_storage Storage object
- */
- public function get_storage()
- {
- // already initialized
- if (!is_object($this->storage)) {
- $this->storage_init();
- }
+ $this->mc_available += intval($this->memcache->addServer(
+ $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
+ }
- return $this->storage;
- }
+ // test connection and failover (will result in $this->mc_available == 0 on complete failure)
+ $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
+ if (!$this->mc_available) {
+ $this->memcache = false;
+ }
+ }
- /**
- * Initialize storage object
- */
- public function storage_init()
- {
- // already initialized
- if (is_object($this->storage)) {
- return;
+ return $this->memcache;
}
- $driver = $this->config->get('storage_driver', 'imap');
- $driver_class = "rcube_{$driver}";
- if (!class_exists($driver_class)) {
- self::raise_error(array(
- 'code' => 700, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Storage driver class ($driver) not found!"),
- true, true);
+ /**
+ * Callback for memcache failure
+ */
+ public function memcache_failure($host, $port)
+ {
+ static $seen = array();
+
+ // only report once
+ if (!$seen["$host:$port"]++) {
+ $this->mc_available--;
+ self::raise_error(array(
+ 'code' => 604, 'type' => 'db',
+ 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => "Memcache failure on host $host:$port"),
+ true, false);
+ }
}
- // Initialize storage object
- $this->storage = new $driver_class;
- // for backward compat. (deprecated, will be removed)
- $this->imap = $this->storage;
+ /**
+ * Initialize and get cache object
+ *
+ * @param string $name Cache identifier
+ * @param string $type Cache type ('db', 'apc' or 'memcache')
+ * @param string $ttl Expiration time for cache items
+ * @param bool $packed Enables/disables data serialization
+ *
+ * @return rcube_cache Cache object
+ */
+ public function get_cache($name, $type='db', $ttl=0, $packed=true)
+ {
+ if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) {
+ $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed);
+ }
- // enable caching of mail data
- $storage_cache = $this->config->get("{$driver}_cache");
- $messages_cache = $this->config->get('messages_cache');
- // for backward compatybility
- if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
- $storage_cache = 'db';
- $messages_cache = true;
+ return $this->caches[$name];
}
- if ($storage_cache)
- $this->storage->set_caching($storage_cache);
- if ($messages_cache)
- $this->storage->set_messages_caching(true);
- // set pagesize from config
- $pagesize = $this->config->get('mail_pagesize');
- if (!$pagesize) {
- $pagesize = $this->config->get('pagesize', 50);
+ /**
+ * Create SMTP object and connect to server
+ *
+ * @param boolean True if connection should be established
+ */
+ public function smtp_init($connect = false)
+ {
+ $this->smtp = new rcube_smtp();
+
+ if ($connect) {
+ $this->smtp->connect();
+ }
}
- $this->storage->set_pagesize($pagesize);
-
- // set class options
- $options = array(
- 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'),
- 'auth_cid' => $this->config->get("{$driver}_auth_cid"),
- 'auth_pw' => $this->config->get("{$driver}_auth_pw"),
- 'debug' => (bool) $this->config->get("{$driver}_debug"),
- 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"),
- 'timeout' => (int) $this->config->get("{$driver}_timeout"),
- 'skip_deleted' => (bool) $this->config->get('skip_deleted'),
- 'driver' => $driver,
- );
-
- if (!empty($_SESSION['storage_host'])) {
- $options['host'] = $_SESSION['storage_host'];
- $options['user'] = $_SESSION['username'];
- $options['port'] = $_SESSION['storage_port'];
- $options['ssl'] = $_SESSION['storage_ssl'];
- $options['password'] = $this->decrypt($_SESSION['password']);
- $_SESSION[$driver.'_host'] = $_SESSION['storage_host'];
+
+
+ /**
+ * Initialize and get storage object
+ *
+ * @return rcube_storage Storage object
+ */
+ public function get_storage()
+ {
+ // already initialized
+ if (!is_object($this->storage)) {
+ $this->storage_init();
+ }
+
+ return $this->storage;
}
- $options = $this->plugins->exec_hook("storage_init", $options);
- // for backward compat. (deprecated, to be removed)
- $options = $this->plugins->exec_hook("imap_init", $options);
+ /**
+ * Initialize storage object
+ */
+ public function storage_init()
+ {
+ // already initialized
+ if (is_object($this->storage)) {
+ return;
+ }
- $this->storage->set_options($options);
- $this->set_storage_prop();
- }
+ $driver = $this->config->get('storage_driver', 'imap');
+ $driver_class = "rcube_{$driver}";
+ if (!class_exists($driver_class)) {
+ self::raise_error(array(
+ 'code' => 700, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Storage driver class ($driver) not found!"),
+ true, true);
+ }
- /**
- * Set storage parameters.
- * This must be done AFTER connecting to the server!
- */
- protected function set_storage_prop()
- {
- $storage = $this->get_storage();
+ // Initialize storage object
+ $this->storage = new $driver_class;
- $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
+ // for backward compat. (deprecated, will be removed)
+ $this->imap = $this->storage;
- if ($default_folders = $this->config->get('default_folders')) {
- $storage->set_default_folders($default_folders);
- }
- if (isset($_SESSION['mbox'])) {
- $storage->set_folder($_SESSION['mbox']);
+ // enable caching of mail data
+ $storage_cache = $this->config->get("{$driver}_cache");
+ $messages_cache = $this->config->get('messages_cache');
+ // for backward compatybility
+ if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
+ $storage_cache = 'db';
+ $messages_cache = true;
+ }
+
+ if ($storage_cache) {
+ $this->storage->set_caching($storage_cache);
+ }
+ if ($messages_cache) {
+ $this->storage->set_messages_caching(true);
+ }
+
+ // set pagesize from config
+ $pagesize = $this->config->get('mail_pagesize');
+ if (!$pagesize) {
+ $pagesize = $this->config->get('pagesize', 50);
+ }
+ $this->storage->set_pagesize($pagesize);
+
+ // set class options
+ $options = array(
+ 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'),
+ 'auth_cid' => $this->config->get("{$driver}_auth_cid"),
+ 'auth_pw' => $this->config->get("{$driver}_auth_pw"),
+ 'debug' => (bool) $this->config->get("{$driver}_debug"),
+ 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"),
+ 'timeout' => (int) $this->config->get("{$driver}_timeout"),
+ 'skip_deleted' => (bool) $this->config->get('skip_deleted'),
+ 'driver' => $driver,
+ );
+
+ if (!empty($_SESSION['storage_host'])) {
+ $options['host'] = $_SESSION['storage_host'];
+ $options['user'] = $_SESSION['username'];
+ $options['port'] = $_SESSION['storage_port'];
+ $options['ssl'] = $_SESSION['storage_ssl'];
+ $options['password'] = $this->decrypt($_SESSION['password']);
+ $_SESSION[$driver.'_host'] = $_SESSION['storage_host'];
+ }
+
+ $options = $this->plugins->exec_hook("storage_init", $options);
+
+ // for backward compat. (deprecated, to be removed)
+ $options = $this->plugins->exec_hook("imap_init", $options);
+
+ $this->storage->set_options($options);
+ $this->set_storage_prop();
}
- if (isset($_SESSION['page'])) {
- $storage->set_page($_SESSION['page']);
+
+
+ /**
+ * Set storage parameters.
+ * This must be done AFTER connecting to the server!
+ */
+ protected function set_storage_prop()
+ {
+ $storage = $this->get_storage();
+
+ $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
+
+ if ($default_folders = $this->config->get('default_folders')) {
+ $storage->set_default_folders($default_folders);
+ }
+ if (isset($_SESSION['mbox'])) {
+ $storage->set_folder($_SESSION['mbox']);
+ }
+ if (isset($_SESSION['page'])) {
+ $storage->set_page($_SESSION['page']);
+ }
}
- }
/**
* Create session object and start the session.
*/
public function session_init()
{
// session started (Installer?)
if (session_id()) {
return;
}
$sess_name = $this->config->get('session_name');
$sess_domain = $this->config->get('session_domain');
$lifetime = $this->config->get('session_lifetime', 0) * 60;
// set session domain
if ($sess_domain) {
ini_set('session.cookie_domain', $sess_domain);
}
// set session garbage collecting time according to session_lifetime
if ($lifetime) {
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
ini_set('session.cookie_secure', rcube_utils::https_check());
ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.serialize_handler', 'php');
ini_set('session.cookie_httponly', 1);
// use database for storing session data
$this->session = new rcube_session($this->get_dbh(), $this->config);
$this->session->register_gc_handler(array($this, 'temp_gc'));
$this->session->register_gc_handler(array($this, 'cache_gc'));
// start PHP session (if not in CLI mode)
if ($_SERVER['REMOTE_ADDR']) {
session_start();
}
}
/**
* Configure session object internals
*/
public function session_configure()
{
if (!$this->session) {
return;
}
$lifetime = $this->config->get('session_lifetime', 0) * 60;
$keep_alive = $this->config->get('keep_alive');
// set keep-alive/check-recent interval
if ($keep_alive) {
// be sure that it's less than session lifetime
if ($lifetime) {
$keep_alive = min($keep_alive, $lifetime - 30);
}
$keep_alive = max(60, $keep_alive);
$this->session->set_keep_alive($keep_alive);
}
$this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME']));
$this->session->set_ip_check($this->config->get('ip_check'));
}
/**
* Garbage collector function for temp files.
* Remove temp files older than two days
*/
public function temp_gc()
{
$tmp = unslashify($this->config->get('temp_dir'));
$expire = time() - 172800; // expire in 48 hours
if ($tmp && ($dir = opendir($tmp))) {
while (($fname = readdir($dir)) !== false) {
if ($fname{0} == '.') {
continue;
}
if (filemtime($tmp.'/'.$fname) < $expire) {
@unlink($tmp.'/'.$fname);
}
}
closedir($dir);
}
}
/**
* Garbage collector for cache entries.
* Set flag to expunge caches on shutdown
*/
public function cache_gc()
{
// because this gc function is called before storage is initialized,
// we just set a flag to expunge storage cache on shutdown.
$this->expunge_cache = true;
}
- /**
- * Get localized text in the desired language
- *
- * @param mixed $attrib Named parameters array or label name
- * @param string $domain Label domain (plugin) name
- *
- * @return string Localized text
- */
- public function gettext($attrib, $domain=null)
- {
- // load localization files if not done yet
- if (empty($this->texts))
- $this->load_language();
-
- // extract attributes
- if (is_string($attrib))
- $attrib = array('name' => $attrib);
-
- $name = $attrib['name'] ? $attrib['name'] : '';
-
- // attrib contain text values: use them from now
- if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))
- $this->texts[$name] = $setval;
-
- // check for text with domain
- if ($domain && ($text = $this->texts[$domain.'.'.$name]))
- ;
- // text does not exist
- else if (!($text = $this->texts[$name])) {
- return "[$name]";
- }
+ /**
+ * Get localized text in the desired language
+ *
+ * @param mixed $attrib Named parameters array or label name
+ * @param string $domain Label domain (plugin) name
+ *
+ * @return string Localized text
+ */
+ public function gettext($attrib, $domain=null)
+ {
+ // load localization files if not done yet
+ if (empty($this->texts)) {
+ $this->load_language();
+ }
- // replace vars in text
- if (is_array($attrib['vars'])) {
- foreach ($attrib['vars'] as $var_key => $var_value)
- $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text);
- }
+ // extract attributes
+ if (is_string($attrib)) {
+ $attrib = array('name' => $attrib);
+ }
- // format output
- if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
- return ucfirst($text);
- else if ($attrib['uppercase'])
- return mb_strtoupper($text);
- else if ($attrib['lowercase'])
- return mb_strtolower($text);
-
- return strtr($text, array('\n' => "\n"));
- }
-
-
- /**
- * Check if the given text label exists
- *
- * @param string $name Label name
- * @param string $domain Label domain (plugin) name or '*' for all domains
- * @param string $ref_domain Sets domain name if label is found
- *
- * @return boolean True if text exists (either in the current language or in en_US)
- */
- public function text_exists($name, $domain = null, &$ref_domain = null)
- {
- // load localization files if not done yet
- if (empty($this->texts))
- $this->load_language();
-
- if (isset($this->texts[$name])) {
- $ref_domain = '';
- return true;
- }
+ $name = $attrib['name'] ? $attrib['name'] : '';
+
+ // attrib contain text values: use them from now
+ if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) {
+ $this->texts[$name] = $setval;
+ }
+
+ // check for text with domain
+ if ($domain && ($text = $this->texts[$domain.'.'.$name])) {
+ }
+ // text does not exist
+ else if (!($text = $this->texts[$name])) {
+ return "[$name]";
+ }
+
+ // replace vars in text
+ if (is_array($attrib['vars'])) {
+ foreach ($attrib['vars'] as $var_key => $var_value) {
+ $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text);
+ }
+ }
- // any of loaded domains (plugins)
- if ($domain == '*') {
- foreach ($this->plugins->loaded_plugins() as $domain)
- if (isset($this->texts[$domain.'.'.$name])) {
- $ref_domain = $domain;
- return true;
+ // format output
+ if (($attrib['uppercase'] && strtolower($attrib['uppercase'] == 'first')) || $attrib['ucfirst']) {
+ return ucfirst($text);
+ }
+ else if ($attrib['uppercase']) {
+ return mb_strtoupper($text);
}
+ else if ($attrib['lowercase']) {
+ return mb_strtolower($text);
+ }
+
+ return strtr($text, array('\n' => "\n"));
}
- // specified domain
- else if ($domain) {
- $ref_domain = $domain;
- return isset($this->texts[$domain.'.'.$name]);
+
+
+ /**
+ * Check if the given text label exists
+ *
+ * @param string $name Label name
+ * @param string $domain Label domain (plugin) name or '*' for all domains
+ * @param string $ref_domain Sets domain name if label is found
+ *
+ * @return boolean True if text exists (either in the current language or in en_US)
+ */
+ public function text_exists($name, $domain = null, &$ref_domain = null)
+ {
+ // load localization files if not done yet
+ if (empty($this->texts)) {
+ $this->load_language();
+ }
+
+ if (isset($this->texts[$name])) {
+ $ref_domain = '';
+ return true;
+ }
+
+ // any of loaded domains (plugins)
+ if ($domain == '*') {
+ foreach ($this->plugins->loaded_plugins() as $domain) {
+ if (isset($this->texts[$domain.'.'.$name])) {
+ $ref_domain = $domain;
+ return true;
+ }
+ }
+ }
+ // specified domain
+ else if ($domain) {
+ $ref_domain = $domain;
+ return isset($this->texts[$domain.'.'.$name]);
+ }
+
+ return false;
}
- return false;
- }
- /**
- * Load a localization package
- *
- * @param string Language ID
- */
- public function load_language($lang = null, $add = array())
- {
- $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
+ /**
+ * Load a localization package
+ *
+ * @param string Language ID
+ * @param array Additional text labels/messages
+ */
+ public function load_language($lang = null, $add = array())
+ {
+ $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
+
+ // load localized texts
+ if (empty($this->texts) || $lang != $_SESSION['language']) {
+ $this->texts = array();
- // load localized texts
- if (empty($this->texts) || $lang != $_SESSION['language']) {
- $this->texts = array();
+ // handle empty lines after closing PHP tag in localization files
+ ob_start();
- // handle empty lines after closing PHP tag in localization files
- ob_start();
+ // get english labels (these should be complete)
+ @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
+ @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
- // get english labels (these should be complete)
- @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
- @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
+ if (is_array($labels))
+ $this->texts = $labels;
+ if (is_array($messages))
+ $this->texts = array_merge($this->texts, $messages);
- if (is_array($labels))
- $this->texts = $labels;
- if (is_array($messages))
- $this->texts = array_merge($this->texts, $messages);
+ // include user language files
+ if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
+ include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');
+ include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');
- // include user language files
- if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
- include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');
- include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');
+ if (is_array($labels))
+ $this->texts = array_merge($this->texts, $labels);
+ if (is_array($messages))
+ $this->texts = array_merge($this->texts, $messages);
+ }
- if (is_array($labels))
- $this->texts = array_merge($this->texts, $labels);
- if (is_array($messages))
- $this->texts = array_merge($this->texts, $messages);
- }
+ ob_end_clean();
- ob_end_clean();
+ $_SESSION['language'] = $lang;
+ }
- $_SESSION['language'] = $lang;
+ // append additional texts (from plugin)
+ if (is_array($add) && !empty($add)) {
+ $this->texts += $add;
+ }
}
- // append additional texts (from plugin)
- if (is_array($add) && !empty($add))
- $this->texts += $add;
- }
-
-
- /**
- * Check the given string and return a valid language code
- *
- * @param string Language code
- * @return string Valid language code
- */
- protected function language_prop($lang)
- {
- static $rcube_languages, $rcube_language_aliases;
-
- // user HTTP_ACCEPT_LANGUAGE if no language is specified
- if (empty($lang) || $lang == 'auto') {
- $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
- $lang = str_replace('-', '_', $accept_langs[0]);
- }
-
- if (empty($rcube_languages)) {
- @include(INSTALL_PATH . 'program/localization/index.inc');
- }
- // check if we have an alias for that language
- if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
- $lang = $rcube_language_aliases[$lang];
+ /**
+ * Check the given string and return a valid language code
+ *
+ * @param string Language code
+ *
+ * @return string Valid language code
+ */
+ protected function language_prop($lang)
+ {
+ static $rcube_languages, $rcube_language_aliases;
+
+ // user HTTP_ACCEPT_LANGUAGE if no language is specified
+ if (empty($lang) || $lang == 'auto') {
+ $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
+ $lang = str_replace('-', '_', $accept_langs[0]);
+ }
+
+ if (empty($rcube_languages)) {
+ @include(INSTALL_PATH . 'program/localization/index.inc');
+ }
+
+ // check if we have an alias for that language
+ if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
+ $lang = $rcube_language_aliases[$lang];
+ }
+ // try the first two chars
+ else if (!isset($rcube_languages[$lang])) {
+ $short = substr($lang, 0, 2);
+
+ // check if we have an alias for the short language code
+ if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
+ $lang = $rcube_language_aliases[$short];
+ }
+ // expand 'nn' to 'nn_NN'
+ else if (!isset($rcube_languages[$short])) {
+ $lang = $short.'_'.strtoupper($short);
+ }
+ }
+
+ if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
+ $lang = 'en_US';
+ }
+
+ return $lang;
}
- // try the first two chars
- else if (!isset($rcube_languages[$lang])) {
- $short = substr($lang, 0, 2);
-
- // check if we have an alias for the short language code
- if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
- $lang = $rcube_language_aliases[$short];
- }
- // expand 'nn' to 'nn_NN'
- else if (!isset($rcube_languages[$short])) {
- $lang = $short.'_'.strtoupper($short);
- }
+
+
+ /**
+ * Read directory program/localization and return a list of available languages
+ *
+ * @return array List of available localizations
+ */
+ public function list_languages()
+ {
+ static $sa_languages = array();
+
+ if (!sizeof($sa_languages)) {
+ @include(INSTALL_PATH . 'program/localization/index.inc');
+
+ if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
+ while (($name = readdir($dh)) !== false) {
+ if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) {
+ continue;
+ }
+
+ if ($label = $rcube_languages[$name]) {
+ $sa_languages[$name] = $label;
+ }
+ }
+ closedir($dh);
+ }
+ }
+
+ return $sa_languages;
}
- if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
- $lang = 'en_US';
+
+ /**
+ * Encrypt using 3DES
+ *
+ * @param string $clear clear text input
+ * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
+ * @param boolean $base64 whether or not to base64_encode() the result before returning
+ *
+ * @return string encrypted text
+ */
+ public function encrypt($clear, $key = 'des_key', $base64 = true)
+ {
+ if (!$clear) {
+ return '';
+ }
+
+ /*-
+ * Add a single canary byte to the end of the clear text, which
+ * will help find out how much of padding will need to be removed
+ * upon decryption; see http://php.net/mcrypt_generic#68082
+ */
+ $clear = pack("a*H2", $clear, "80");
+
+ if (function_exists('mcrypt_module_open') &&
+ ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))
+ ) {
+ $iv = $this->create_iv(mcrypt_enc_get_iv_size($td));
+ mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
+ $cipher = $iv . mcrypt_generic($td, $clear);
+ mcrypt_generic_deinit($td);
+ mcrypt_module_close($td);
+ }
+ else {
+ @include_once 'des.inc';
+
+ if (function_exists('des')) {
+ $des_iv_size = 8;
+ $iv = $this->create_iv($des_iv_size);
+ $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);
+ }
+ else {
+ self::raise_error(array(
+ 'code' => 500, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available"
+ ), true, true);
+ }
+ }
+
+ return $base64 ? base64_encode($cipher) : $cipher;
}
- return $lang;
- }
+ /**
+ * Decrypt 3DES-encrypted string
+ *
+ * @param string $cipher encrypted text
+ * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
+ * @param boolean $base64 whether or not input is base64-encoded
+ *
+ * @return string decrypted text
+ */
+ public function decrypt($cipher, $key = 'des_key', $base64 = true)
+ {
+ if (!$cipher) {
+ return '';
+ }
- /**
- * Read directory program/localization and return a list of available languages
- *
- * @return array List of available localizations
- */
- public function list_languages()
- {
- static $sa_languages = array();
+ $cipher = $base64 ? base64_decode($cipher) : $cipher;
- if (!sizeof($sa_languages)) {
- @include(INSTALL_PATH . 'program/localization/index.inc');
+ if (function_exists('mcrypt_module_open') &&
+ ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))
+ ) {
+ $iv_size = mcrypt_enc_get_iv_size($td);
+ $iv = substr($cipher, 0, $iv_size);
- if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
- while (($name = readdir($dh)) !== false) {
- if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))
- continue;
+ // session corruption? (#1485970)
+ if (strlen($iv) < $iv_size) {
+ return '';
+ }
- if ($label = $rcube_languages[$name])
- $sa_languages[$name] = $label;
+ $cipher = substr($cipher, $iv_size);
+ mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
+ $clear = mdecrypt_generic($td, $cipher);
+ mcrypt_generic_deinit($td);
+ mcrypt_module_close($td);
+ }
+ else {
+ @include_once 'des.inc';
+
+ if (function_exists('des')) {
+ $des_iv_size = 8;
+ $iv = substr($cipher, 0, $des_iv_size);
+ $cipher = substr($cipher, $des_iv_size);
+ $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);
+ }
+ else {
+ self::raise_error(array(
+ 'code' => 500, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"
+ ), true, true);
+ }
}
- closedir($dh);
- }
+
+ /*-
+ * Trim PHP's padding and the canary byte; see note in
+ * rcube::encrypt() and http://php.net/mcrypt_generic#68082
+ */
+ $clear = substr(rtrim($clear, "\0"), 0, -1);
+
+ return $clear;
}
- return $sa_languages;
- }
-
-
- /**
- * Encrypt using 3DES
- *
- * @param string $clear clear text input
- * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
- * @param boolean $base64 whether or not to base64_encode() the result before returning
- *
- * @return string encrypted text
- */
- public function encrypt($clear, $key = 'des_key', $base64 = true)
- {
- if (!$clear)
- return '';
-
- /*-
- * Add a single canary byte to the end of the clear text, which
- * will help find out how much of padding will need to be removed
- * upon decryption; see http://php.net/mcrypt_generic#68082
+
+ /**
+ * Generates encryption initialization vector (IV)
+ *
+ * @param int Vector size
+ *
+ * @return string Vector string
*/
- $clear = pack("a*H2", $clear, "80");
-
- if (function_exists('mcrypt_module_open') &&
- ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) {
- $iv = $this->create_iv(mcrypt_enc_get_iv_size($td));
- mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
- $cipher = $iv . mcrypt_generic($td, $clear);
- mcrypt_generic_deinit($td);
- mcrypt_module_close($td);
- }
- else {
- @include_once 'des.inc';
-
- if (function_exists('des')) {
- $des_iv_size = 8;
- $iv = $this->create_iv($des_iv_size);
- $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);
- }
- else {
- self::raise_error(array(
- 'code' => 500, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available"
- ), true, true);
- }
+ private function create_iv($size)
+ {
+ // mcrypt_create_iv() can be slow when system lacks entrophy
+ // we'll generate IV vector manually
+ $iv = '';
+ for ($i = 0; $i < $size; $i++) {
+ $iv .= chr(mt_rand(0, 255));
+ }
+
+ return $iv;
}
- return $base64 ? base64_encode($cipher) : $cipher;
- }
-
- /**
- * Decrypt 3DES-encrypted string
- *
- * @param string $cipher encrypted text
- * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
- * @param boolean $base64 whether or not input is base64-encoded
- *
- * @return string decrypted text
- */
- public function decrypt($cipher, $key = 'des_key', $base64 = true)
- {
- if (!$cipher)
- return '';
-
- $cipher = $base64 ? base64_decode($cipher) : $cipher;
-
- if (function_exists('mcrypt_module_open') &&
- ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) {
- $iv_size = mcrypt_enc_get_iv_size($td);
- $iv = substr($cipher, 0, $iv_size);
-
- // session corruption? (#1485970)
- if (strlen($iv) < $iv_size)
- return '';
- $cipher = substr($cipher, $iv_size);
- mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
- $clear = mdecrypt_generic($td, $cipher);
- mcrypt_generic_deinit($td);
- mcrypt_module_close($td);
- }
- else {
- @include_once 'des.inc';
-
- if (function_exists('des')) {
- $des_iv_size = 8;
- $iv = substr($cipher, 0, $des_iv_size);
- $cipher = substr($cipher, $des_iv_size);
- $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);
- }
- else {
- self::raise_error(array(
- 'code' => 500, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"
- ), true, true);
- }
+ /**
+ * Build a valid URL to this instance of Roundcube
+ *
+ * @param mixed Either a string with the action or url parameters as key-value pairs
+ * @return string Valid application URL
+ */
+ public function url($p)
+ {
+ // STUB: should be overloaded by the application
+ return '';
}
- /*-
- * Trim PHP's padding and the canary byte; see note in
- * rcube::encrypt() and http://php.net/mcrypt_generic#68082
+
+ /**
+ * Function to be executed in script shutdown
+ * Registered with register_shutdown_function()
*/
- $clear = substr(rtrim($clear, "\0"), 0, -1);
-
- return $clear;
- }
-
- /**
- * Generates encryption initialization vector (IV)
- *
- * @param int Vector size
- * @return string Vector string
- */
- private function create_iv($size)
- {
- // mcrypt_create_iv() can be slow when system lacks entrophy
- // we'll generate IV vector manually
- $iv = '';
- for ($i = 0; $i < $size; $i++)
- $iv .= chr(mt_rand(0, 255));
- return $iv;
- }
-
-
- /**
- * Build a valid URL to this instance of Roundcube
- *
- * @param mixed Either a string with the action or url parameters as key-value pairs
- * @return string Valid application URL
- */
- public function url($p)
- {
- // STUB: should be overloaded by the application
- return '';
- }
-
-
- /**
- * Function to be executed in script shutdown
- * Registered with register_shutdown_function()
- */
- public function shutdown()
- {
- foreach ($this->shutdown_functions as $function)
- call_user_func($function);
-
- if (is_object($this->smtp))
- $this->smtp->disconnect();
-
- foreach ($this->caches as $cache) {
- if (is_object($cache))
- $cache->close();
- }
+ public function shutdown()
+ {
+ foreach ($this->shutdown_functions as $function) {
+ call_user_func($function);
+ }
+
+ if (is_object($this->smtp)) {
+ $this->smtp->disconnect();
+ }
+
+ foreach ($this->caches as $cache) {
+ if (is_object($cache)) {
+ $cache->close();
+ }
+ }
- if (is_object($this->storage)) {
- if ($this->expunge_cache)
- $this->storage->expunge_cache();
- $this->storage->close();
+ if (is_object($this->storage)) {
+ if ($this->expunge_cache) {
+ $this->storage->expunge_cache();
+ }
+ $this->storage->close();
+ }
}
- }
-
-
- /**
- * Registers shutdown function to be executed on shutdown.
- * The functions will be executed before destroying any
- * objects like smtp, imap, session, etc.
- *
- * @param callback Function callback
- */
- public function add_shutdown_function($function)
- {
- $this->shutdown_functions[] = $function;
- }
-
-
- /**
- * Construct shell command, execute it and return output as string.
- * Keywords {keyword} are replaced with arguments
- *
- * @param $cmd Format string with {keywords} to be replaced
- * @param $values (zero, one or more arrays can be passed)
- * @return output of command. shell errors not detectable
- */
- public static function exec(/* $cmd, $values1 = array(), ... */)
- {
- $args = func_get_args();
- $cmd = array_shift($args);
- $values = $replacements = array();
-
- // merge values into one array
- foreach ($args as $arg)
- $values += (array)$arg;
-
- preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER);
- foreach ($matches as $tags) {
- list(, $tag, $option, $key) = $tags;
- $parts = array();
-
- if ($option) {
- foreach ((array)$values["-$key"] as $key => $value) {
- if ($value === true || $value === false || $value === null)
- $parts[] = $value ? $key : "";
- else foreach ((array)$value as $val)
- $parts[] = "$key " . escapeshellarg($val);
- }
- }
- else {
- foreach ((array)$values[$key] as $value)
- $parts[] = escapeshellarg($value);
- }
-
- $replacements[$tag] = join(" ", $parts);
+
+
+ /**
+ * Registers shutdown function to be executed on shutdown.
+ * The functions will be executed before destroying any
+ * objects like smtp, imap, session, etc.
+ *
+ * @param callback Function callback
+ */
+ public function add_shutdown_function($function)
+ {
+ $this->shutdown_functions[] = $function;
}
- // use strtr behaviour of going through source string once
- $cmd = strtr($cmd, $replacements);
- return (string)shell_exec($cmd);
- }
+ /**
+ * Construct shell command, execute it and return output as string.
+ * Keywords {keyword} are replaced with arguments
+ *
+ * @param $cmd Format string with {keywords} to be replaced
+ * @param $values (zero, one or more arrays can be passed)
+ *
+ * @return output of command. shell errors not detectable
+ */
+ public static function exec(/* $cmd, $values1 = array(), ... */)
+ {
+ $args = func_get_args();
+ $cmd = array_shift($args);
+ $values = $replacements = array();
+
+ // merge values into one array
+ foreach ($args as $arg) {
+ $values += (array)$arg;
+ }
+
+ preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER);
+ foreach ($matches as $tags) {
+ list(, $tag, $option, $key) = $tags;
+ $parts = array();
+
+ if ($option) {
+ foreach ((array)$values["-$key"] as $key => $value) {
+ if ($value === true || $value === false || $value === null) {
+ $parts[] = $value ? $key : "";
+ }
+ else {
+ foreach ((array)$value as $val) {
+ $parts[] = "$key " . escapeshellarg($val);
+ }
+ }
+ }
+ }
+ else {
+ foreach ((array)$values[$key] as $value) {
+ $parts[] = escapeshellarg($value);
+ }
+ }
+
+ $replacements[$tag] = join(" ", $parts);
+ }
+
+ // use strtr behaviour of going through source string once
+ $cmd = strtr($cmd, $replacements);
+
+ return (string)shell_exec($cmd);
+ }
/**
* Print or write debug messages
*
* @param mixed Debug message or data
*/
public static function console()
{
$args = func_get_args();
if (class_exists('rcube', false)) {
$rcube = self::get_instance();
$plugin = $rcube->plugins->exec_hook('console', array('args' => $args));
if ($plugin['abort']) {
return;
}
$args = $plugin['args'];
}
$msg = array();
foreach ($args as $arg) {
$msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
}
self::write_log('console', join(";\n", $msg));
}
/**
* Append a line to a logfile in the logs directory.
* Date will be added automatically to the line.
*
* @param $name name of log file
* @param line Line to append
*/
public static function write_log($name, $line)
{
if (!is_string($line)) {
$line = var_export($line, true);
}
$date_format = self::$instance ? self::$instance->config->get('log_date_format') : null;
$log_driver = self::$instance ? self::$instance->config->get('log_driver') : null;
if (empty($date_format)) {
$date_format = 'd-M-Y H:i:s O';
}
$date = date($date_format);
// trigger logging hook
if (is_object(self::$instance) && is_object(self::$instance->plugins)) {
$log = self::$instance->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line));
$name = $log['name'];
$line = $log['line'];
$date = $log['date'];
if ($log['abort'])
return true;
}
if ($log_driver == 'syslog') {
$prio = $name == 'errors' ? LOG_ERR : LOG_INFO;
syslog($prio, $line);
return true;
}
// log_driver == 'file' is assumed here
$line = sprintf("[%s]: %s\n", $date, $line);
$log_dir = self::$instance ? self::$instance->config->get('log_dir') : null;
if (empty($log_dir)) {
$log_dir = INSTALL_PATH . 'logs';
}
// try to open specific log file for writing
$logfile = $log_dir.'/'.$name;
if ($fp = @fopen($logfile, 'a')) {
fwrite($fp, $line);
fflush($fp);
fclose($fp);
return true;
}
trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING);
return false;
}
/**
* Throw system error (and show error page).
*
* @param array Named parameters
* - code: Error code (required)
* - type: Error type [php|db|imap|javascript] (required)
* - message: Error message
* - file: File where error occured
* - line: Line where error occured
* @param boolean True to log the error
* @param boolean Terminate script execution
*/
public static function raise_error($arg = array(), $log = false, $terminate = false)
{
// handle PHP exceptions
if (is_object($arg) && is_a($arg, 'Exception')) {
$err = array(
'type' => 'php',
'code' => $arg->getCode(),
'line' => $arg->getLine(),
'file' => $arg->getFile(),
'message' => $arg->getMessage(),
);
$arg = $err;
}
// installer
if (class_exists('rcube_install', false)) {
$rci = rcube_install::get_instance();
$rci->raise_error($arg);
return;
}
if (($log || $terminate) && $arg['type'] && $arg['message']) {
$arg['fatal'] = $terminate;
self::log_bug($arg);
}
// display error page and terminate script
if ($terminate && is_object(self::$instance->output)) {
self::$instance->output->raise_error($arg['code'], $arg['message']);
}
}
/**
* Report error according to configured debug_level
*
* @param array Named parameters
* @see self::raise_error()
*/
public static function log_bug($arg_arr)
{
$program = strtoupper($arg_arr['type']);
$level = self::get_instance()->config->get('debug_level');
// disable errors for ajax requests, write to log instead (#1487831)
if (($level & 4) && !empty($_REQUEST['_remote'])) {
$level = ($level ^ 4) | 1;
}
// write error to local log file
if (($level & 1) || !empty($arg_arr['fatal'])) {
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$post_query = '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']);
}
else {
$post_query = '';
}
$log_entry = sprintf("%s Error: %s%s (%s %s)",
$program,
$arg_arr['message'],
$arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '',
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'] . $post_query);
if (!self::write_log('errors', $log_entry)) {
// send error to PHPs error handler if write_log didn't succeed
trigger_error($arg_arr['message']);
}
}
// report the bug to the global bug reporting system
if ($level & 2) {
// TODO: Send error via HTTP
}
// show error if debug_mode is on
if ($level & 4) {
print "<b>$program Error";
if (!empty($arg_arr['file']) && !empty($arg_arr['line'])) {
print " in $arg_arr[file] ($arg_arr[line])";
}
print ':</b>&nbsp;';
print nl2br($arg_arr['message']);
print '<br />';
flush();
}
}
/**
* Returns current time (with microseconds).
*
* @return float Current time in seconds since the Unix
*/
public static function timer()
{
return microtime(true);
}
/**
* Logs time difference according to provided timer
*
* @param float $timer Timer (self::timer() result)
* @param string $label Log line prefix
* @param string $dest Log file name
*
* @see self::timer()
*/
public static function print_timer($timer, $label = 'Timer', $dest = 'console')
{
static $print_count = 0;
$print_count++;
$now = self::timer();
$diff = $now - $timer;
if (empty($label)) {
$label = 'Timer '.$print_count;
}
self::write_log($dest, sprintf("%s: %0.4f sec", $label, $diff));
}
/**
* Getter for logged user ID.
*
* @return mixed User identifier
*/
public function get_user_id()
{
if (is_object($this->user)) {
return $this->user->ID;
}
else if (isset($_SESSION['user_id'])) {
return $_SESSION['user_id'];
}
return null;
}
/**
* Getter for logged user name.
*
* @return string User name
*/
public function get_user_name()
{
if (is_object($this->user)) {
return $this->user->get_username();
}
return null;
}
}
/**
* Lightweight plugin API class serving as a dummy if plugins are not enabled
*
* @package Core
*/
class rcube_dummy_plugin_api
{
/**
* Triggers a plugin hook.
* @see rcube_plugin_api::exec_hook()
*/
public function exec_hook($hook, $args = array())
{
return $args;
}
}
diff --git a/program/include/rcube_browser.php b/program/include/rcube_browser.php
index 06033e036..7cfae709d 100644
--- a/program/include/rcube_browser.php
+++ b/program/include/rcube_browser.php
@@ -1,74 +1,72 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/rcube_browser.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2007-2009, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Class representing the client browser's properties |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
- * rcube_browser
- *
* Provide details about the client's browser based on the User-Agent header
*
* @package Core
*/
class rcube_browser
{
function __construct()
{
$HTTP_USER_AGENT = strtolower($_SERVER['HTTP_USER_AGENT']);
$this->ver = 0;
$this->win = strpos($HTTP_USER_AGENT, 'win') != false;
$this->mac = strpos($HTTP_USER_AGENT, 'mac') != false;
$this->linux = strpos($HTTP_USER_AGENT, 'linux') != false;
$this->unix = strpos($HTTP_USER_AGENT, 'unix') != false;
$this->opera = strpos($HTTP_USER_AGENT, 'opera') !== false;
$this->ns4 = strpos($HTTP_USER_AGENT, 'mozilla/4') !== false && strpos($HTTP_USER_AGENT, 'msie') === false;
$this->ns = ($this->ns4 || strpos($HTTP_USER_AGENT, 'netscape') !== false);
$this->ie = !$this->opera && strpos($HTTP_USER_AGENT, 'compatible; msie') !== false;
$this->khtml = strpos($HTTP_USER_AGENT, 'khtml') !== false;
$this->mz = !$this->ie && !$this->khtml && strpos($HTTP_USER_AGENT, 'mozilla/5') !== false;
$this->chrome = strpos($HTTP_USER_AGENT, 'chrome') !== false;
$this->safari = !$this->chrome && ($this->khtml || strpos($HTTP_USER_AGENT, 'safari') !== false);
if ($this->ns || $this->chrome) {
$test = preg_match('/(mozilla|chrome)\/([0-9.]+)/', $HTTP_USER_AGENT, $regs);
$this->ver = $test ? (float)$regs[2] : 0;
}
else if ($this->mz) {
$test = preg_match('/rv:([0-9.]+)/', $HTTP_USER_AGENT, $regs);
$this->ver = $test ? (float)$regs[1] : 0;
}
else if ($this->ie || $this->opera) {
$test = preg_match('/(msie|opera) ([0-9.]+)/', $HTTP_USER_AGENT, $regs);
$this->ver = $test ? (float)$regs[2] : 0;
}
if (preg_match('/ ([a-z]{2})-([a-z]{2})/', $HTTP_USER_AGENT, $regs))
$this->lang = $regs[1];
else
$this->lang = 'en';
$this->dom = ($this->mz || $this->safari || ($this->ie && $this->ver>=5) || ($this->opera && $this->ver>=7));
$this->pngalpha = $this->mz || $this->safari || ($this->ie && $this->ver>=5.5) ||
($this->ie && $this->ver>=5 && $this->mac) || ($this->opera && $this->ver>=7) ? true : false;
$this->imgdata = !$this->ie;
}
}
diff --git a/program/include/rcube_plugin.php b/program/include/rcube_plugin.php
index b1ec32a8a..c1035733b 100644
--- a/program/include/rcube_plugin.php
+++ b/program/include/rcube_plugin.php
@@ -1,359 +1,359 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/rcube_plugin.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2008-2009, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Abstract plugins interface/class |
| All plugins need to extend this class |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Plugin interface class
*
* @package PluginAPI
*/
abstract class rcube_plugin
{
/**
* Class name of the plugin instance
*
* @var string
*/
public $ID;
/**
* Instance of Plugin API
*
* @var rcube_plugin_api
*/
public $api;
/**
* Regular expression defining task(s) to bind with
*
* @var string
*/
public $task;
/**
* Disables plugin in AJAX requests
*
* @var boolean
*/
public $noajax = false;
/**
* Disables plugin in framed mode
*
* @var boolean
*/
public $noframe = false;
protected $home;
protected $urlbase;
private $mytask;
/**
* Default constructor.
*
* @param rcube_plugin_api $api Plugin API
*/
public function __construct($api)
{
$this->ID = get_class($this);
$this->api = $api;
$this->home = $api->dir . $this->ID;
$this->urlbase = $api->url . $this->ID . '/';
}
/**
* Initialization method, needs to be implemented by the plugin itself
*/
abstract function init();
/**
* Attempt to load the given plugin which is required for the current plugin
*
* @param string Plugin name
* @return boolean True on success, false on failure
*/
public function require_plugin($plugin_name)
{
return $this->api->load_plugin($plugin_name);
}
/**
* Load local config file from plugins directory.
* The loaded values are patched over the global configuration.
*
* @param string $fname Config file name relative to the plugin's folder
* @return boolean True on success, false on failure
*/
public function load_config($fname = 'config.inc.php')
{
$fpath = $this->home.'/'.$fname;
$rcube = rcube::get_instance();
if (is_file($fpath) && !$rcube->config->load_from_file($fpath)) {
rcube::raise_error(array(
'code' => 527, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to load config from $fpath"), true, false);
return false;
}
return true;
}
/**
* Register a callback function for a specific (server-side) hook
*
* @param string $hook Hook name
* @param mixed $callback Callback function as string or array with object reference and method name
*/
public function add_hook($hook, $callback)
{
$this->api->register_hook($hook, $callback);
}
/**
* Unregister a callback function for a specific (server-side) hook.
*
* @param string $hook Hook name
* @param mixed $callback Callback function as string or array with object reference and method name
*/
public function remove_hook($hook, $callback)
{
$this->api->unregister_hook($hook, $callback);
}
/**
* Load localized texts from the plugins dir
*
* @param string $dir Directory to search in
* @param mixed $add2client Make texts also available on the client (array with list or true for all)
*/
public function add_texts($dir, $add2client = false)
{
$domain = $this->ID;
$lang = $_SESSION['language'];
$langs = array_unique(array('en_US', $lang));
$locdir = slashify(realpath(slashify($this->home) . $dir));
$texts = array();
// Language aliases used to find localization in similar lang, see below
$aliases = array(
'de_CH' => 'de_DE',
'es_AR' => 'es_ES',
'fa_AF' => 'fa_IR',
'nl_BE' => 'nl_NL',
'pt_BR' => 'pt_PT',
'zh_CN' => 'zh_TW',
);
// use buffering to handle empty lines/spaces after closing PHP tag
ob_start();
foreach ($langs as $lng) {
$fpath = $locdir . $lng . '.inc';
if (is_file($fpath) && is_readable($fpath)) {
include $fpath;
$texts = (array)$labels + (array)$messages + (array)$texts;
}
else if ($lng != 'en_US') {
// Find localization in similar language (#1488401)
$alias = null;
if (!empty($aliases[$lng])) {
$alias = $aliases[$lng];
}
else if ($key = array_search($lng, $aliases)) {
$alias = $key;
}
if (!empty($alias)) {
$fpath = $locdir . $alias . '.inc';
if (is_file($fpath) && is_readable($fpath)) {
include $fpath;
$texts = (array)$labels + (array)$messages + (array)$texts;
}
}
}
}
ob_end_clean();
// prepend domain to text keys and add to the application texts repository
if (!empty($texts)) {
$add = array();
foreach ($texts as $key => $value)
$add[$domain.'.'.$key] = $value;
$rcmail = rcmail::get_instance();
$rcmail->load_language($lang, $add);
// add labels to client
if ($add2client) {
$js_labels = is_array($add2client) ? array_map(array($this, 'label_map_callback'), $add2client) : array_keys($add);
$rcmail->output->add_label($js_labels);
}
}
}
/**
* Wrapper for rcmail::gettext() adding the plugin ID as domain
*
* @param string $p Message identifier
* @return string Localized text
* @see rcmail::gettext()
*/
public function gettext($p)
{
return rcube::get_instance()->gettext($p, $this->ID);
}
/**
* Register this plugin to be responsible for a specific task
*
* @param string $task Task name (only characters [a-z0-9_.-] are allowed)
*/
public function register_task($task)
{
if ($this->api->register_task($task, $this->ID))
$this->mytask = $task;
}
/**
* Register a handler for a specific client-request action
*
* The callback will be executed upon a request like /?_task=mail&_action=plugin.myaction
*
* @param string $action Action name (should be unique)
* @param mixed $callback Callback function as string or array with object reference and method name
*/
public function register_action($action, $callback)
{
$this->api->register_action($action, $this->ID, $callback, $this->mytask);
}
/**
* Register a handler function for a template object
*
* When parsing a template for display, tags like <roundcube:object name="plugin.myobject" />
* will be replaced by the return value if the registered callback function.
*
* @param string $name Object name (should be unique and start with 'plugin.')
* @param mixed $callback Callback function as string or array with object reference and method name
*/
public function register_handler($name, $callback)
{
$this->api->register_handler($name, $this->ID, $callback);
}
/**
* Make this javascipt file available on the client
*
* @param string $fn File path; absolute or relative to the plugin directory
*/
public function include_script($fn)
{
$this->api->include_script($this->resource_url($fn));
}
/**
* Make this stylesheet available on the client
*
* @param string $fn File path; absolute or relative to the plugin directory
*/
public function include_stylesheet($fn)
{
$this->api->include_stylesheet($this->resource_url($fn));
}
/**
* Append a button to a certain container
*
* @param array $p Hash array with named parameters (as used in skin templates)
* @param string $container Container name where the buttons should be added to
* @see rcube_remplate::button()
*/
public function add_button($p, $container)
{
if ($this->api->output->type == 'html') {
// fix relative paths
foreach (array('imagepas', 'imageact', 'imagesel') as $key)
if ($p[$key])
$p[$key] = $this->api->url . $this->resource_url($p[$key]);
$this->api->add_content($this->api->output->button($p), $container);
}
}
/**
* Generate an absolute URL to the given resource within the current
* plugin directory
*
* @param string $fn The file name
* @return string Absolute URL to the given resource
*/
public function url($fn)
{
return $this->api->url . $this->resource_url($fn);
}
/**
* Make the given file name link into the plugin directory
*
* @param string $fn Filename
*/
private function resource_url($fn)
{
if ($fn[0] != '/' && !preg_match('|^https?://|i', $fn))
return $this->ID . '/' . $fn;
else
return $fn;
}
/**
* Provide path to the currently selected skin folder within the plugin directory
* with a fallback to the default skin folder.
*
* @return string Skin path relative to plugins directory
*/
public function local_skin_path()
{
$rcmail = rcube::get_instance();
- foreach (array($rcmail->config->get('skin'),'default') as $skin) {
+ foreach (array($rcmail->config->get('skin'), 'larry') as $skin) {
$skin_path = 'skins/' . $skin;
if (is_dir(realpath(slashify($this->home) . $skin_path)))
break;
}
return $skin_path;
}
/**
* Callback function for array_map
*
* @param string $key Array key.
* @return string
*/
private function label_map_callback($key)
{
return $this->ID.'.'.$key;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 5, 11:51 AM (7 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
427870
Default Alt Text
(184 KB)

Event Timeline