Page MenuHomePhorge

No OneTemporary

Size
376 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/CHANGELOG b/CHANGELOG
index ffdcb87b1..6b0ea400c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,2680 +1,2681 @@
CHANGELOG Roundcube Webmail
===========================
+- Removed referer_check option (#6440)
- Update to TinyMCE 4.8.2
- Plugin API: Added 'raise_error' hook (#6199)
- Managesieve: Added support for 'editheader' extension - RFC5293 (#5954)
- Password: Added 'modoboa' driver (#6361)
- Password: Fix bug where password_dovecotpw_with_method setting could be ignored (#6436)
- Elastic: Improved UX of search dialogs (#6416)
- Fix so invalid smtp_helo_host is never used, fallback to localhost (#6408)
- Fix custom logo size in Elastic (#6424)
- Fix listing the same attachment multiple times on forwarded messages
- Fix compatibility with MySQL 8 - error on 'system' table use
- Managesieve: Fix bug where show_real_foldernames setting wasn't respected (#6422)
RELEASE 1.4-beta
----------------
- Added new skin with mobile support - the Elastic
- Support Redis cache
- Email Resent (Bounce) feature (#4985)
- Improved Mailvelope integration
- Added private key listing and generating to identity settings
- Enable encrypt & sign option if Mailvelope supports it
- Allow contacts without an email address (#5079)
- Support SMTPUTF8 and relax email address validation to support unicode in local part (#5120)
- Support for IMAP folders that cannot contain both folders and messages (#5057)
- Update to jQuery-3.3.1
- Update to jQuery-minicolors 2.2.6
- Update to TinyMCE 4.7.13
- Remove sample PHP configuration from .htaccess and .user.ini files (#5850)
- Extend skin_logo setting to allow per skin logos (#6272)
- Use Masterminds/HTML5 parser for better HTML5 support (#5761)
- Add More actions button in Contacts toolbar with Copy/Move actions (#6081)
- Display an error when clicking disabled link to register protocol handler (#6079)
- Add option trusted_host_patterns (#6009, #5752)
- Support additional connect parameters in PostgreSQL database wrapper
- Use UI dialogs instead of confirm() and alert() where possible
- Display value of the SMTP message size limit in the error message (#6032)
- Show message flagged status in message view (#5080)
- Skip redundant INSERT query on successful logon when using PHP7
- Replace display_version with display_product_version (#5904)
- Extend disabled_actions config so it accepts also button names (#5903)
- Handle remote stylesheets the same as remote images, ask the user to allow them (#5994)
- Add Message-ID to the sendmail log (#5871)
- Add option to hide folders in share/other-user namespace or outside of the personal namespace root (#5073)
- Archive: Fix archiving by sender address on cyrus-imap
- Archive: Style Archive folder also on folder selector and folder manager lists
- Archive: Add Thunderbird compatible Month option (#5623)
- Archive: Create archive folder automatically if it's configured, but does not exist (#6076)
- Enigma: Add button to send mail unencrypted if no key was found (#5913)
- Enigma: Add options to set PGP cipher/digest algorithms (#5645)
- Enigma: Multi-host support
- Managesieve: Add ability to disable filter sets and other actions (#5496, #5898)
- Managesieve: Add option managesieve_forward to enable settings dialog for simple forwarding (#6021)
- Managesieve: Support filter action with custom IMAP flags (#6011)
- Managesieve: Support 'mime' extension tests - RFC5703 (#5832)
- Managesieve: Support GSSAPI authentication with krb_authentication plugin (#5779)
- Managesieve: Support enabling the plugin for specified hosts only (#6292)
- Password: Support host variables in password_db_dsn option (#5955)
- Password: Automatic virtualmin domain setting, removed password_virtualmin_format option (#5759)
- Password: Added password_username_format option (#5766)
- subscriptions_option: show \\Noselect folders greyed out (#5621)
- zipdownload: Added option to define size limit for multiple messages download (#5696)
- vcard_attachments: Add possibility to send contact vCard from Contacts toolbar (#6080)
- Changed defaults for smtp_user (%u), smtp_pass (%p) and smtp_port (587)
- Composer: Fix certificate validation errors by using packagist only (#5148)
- Add --get and --extract arguments and CACHEDIR env-variable support to install-jsdeps.sh (#5882)
- Support _filter and _scope as GET arguments for opening mail UI (#5825)
- Various improvements for templating engine and skin behaviours
- Support conditional include
- Support for 'link' objects
- Support including files with path relative to templates directory
- Use <button> instead of <input> for submit button on logon screen
- Support skin localization (#5853)
- Reset onerror on images if placeholder does not exist to prevent from requests storm
- Unified and simplified code for loading content frame for responses and identities
- Display contact import and advanced search in popup dialogs
- Display a dialog for mail import with supported format description and upload size hint
- Make possible to set (some) config options from a skin
- Added optional checkbox selection for the list widget
- Make 'compose' command always enabled
- Add .log suffix to all log file names, add option log_file_ext to control this (#313)
- Return "401 Unauthorized" status when login fails (#5663)
- Support both comma and semicolon as recipient separator, drop recipients_separator option (#5092)
- Plugin API: Added 'show_bytes' hook (#5001)
- Add option to not indent quoted text on top-posting reply (#5105)
- Removed global $CONFIG variable
- Removed debug_level setting
- Support AUTHENTICATE LOGIN for IMAP connections (#5563)
- Support LDAP GSSAPI authentication (#5703)
- Localized timezone selector (#4983)
- Use 7bit encoding for ISO-2022-* charsets in sent mail (#5640)
- Handle inline images also inside multipart/mixed messages (#5905)
- Allow style tags in HTML editor on composed/reply messages (#5751)
- Use Github API as a fallback to fetch js dependencies to workaround throttling issues (#6248)
- Show confirm dialog when moving folders using drag and drop (#6119)
- Fix bug where new_user_dialog email check could have been circumvented by deleting / abandoning session (#5929)
- Fix skin extending for assets (#5115)
- Fix handling of forwarded messages inside of a TNEF message (#5632)
- Fix bug where attachment size wasn't visible when the filename was too long (#6033)
- Fix checking table columns when there's more schemas/databases in postgres/mysql (#6047)
- Fix css conflicts in user interface and e-mail content (#5891)
- Fix duplicated signature when using Back button in Chrome (#5809)
- Fix touch event issue on messages list in IE/Edge (#5781)
- Fix so links over images are not removed in plain text signatures converted from HTML (#4473)
- Fix various issues when downloading files with names containing non-ascii chars, use RFC 2231 (#5772)
- Fix PHP warnings on dummy QUOTA responses in Courier-IMAP 4.17.1 (#6374)
- Fix so fallback from BINARY to BODY FETCH is used also on [PARSE] errors in dovecot 2.3 (#6383)
- Enigma: Fix deleting keys with authentication subkeys (#6381)
- Fix invalid regular expressions that throw warnings on PHP 7.3 (#6398)
- Fix so Classic skin splitter does not escape out of window (#6397)
- Fix XSS issue in handling invalid style tag content (#6410)
RELEASE 1.3.7
-------------
- Fix PHP Warning: Use of undefined constant IDNA_DEFAULT on systems without php-intl (#6244)
- Fix bug where some parts of quota information could have been ignored (#6280)
- Fix bug where some escape sequences in html styles could bypass security checks
- Fix bug where some forbidden characters on Cyrus-IMAP were not prevented from use in folder names
- Fix bug where only attachments with the same name would be ignored on zip download (#6301)
- Fix bug where unicode contact names could have been broken/emptied or caused DB errors (#6299)
- Fix bug where after "mark all folders as read" action message counters were not reset (#6307)
- Enigma: [EFAIL] Don't decrypt PGP messages with no MDC protection (#6289)
- Fix bug where some HTML comments could have been malformed by HTML parser (#6333)
RELEASE 1.3.6
-------------
- Fix parsing date strings (e.g. from a Date: mail header) with comments (#6216)
- Fix PHP 7.2: count(): Parameter must be an array in enchant-based spellchecker (#6234)
- Fix possible IMAP command injection and type juggling vulnerabilities (#6229)
- Enigma: Fix key selection for signing
- Enigma: Enable keypair generation on Internet Explorer 11
- Fix check_request() bypass in places using get_uids() [CVE-2018-9846] (#6238)
- Fix bug where usernames without domain part could be malformed or converted to lower-case on logon (#6224)
RELEASE 1.3.5
-------------
- Managesieve: Fix bug where text: syntax was forced for strings longer than 1024 characters (#6143)
- Managesieve: Fix missing Save button in Edit Filter Set page of Classic skin (#6154)
- Fix duplicated labels in Test SMTP Config section (#6166)
- Fix PHP Warning: exif_read_data(...): Illegal IFD size (#6169)
- Enigma: Fix key generation in Safari by upgrade to OpenPGP 2.6.2 (#6149)
- Fix security issue in remote content blocking on HTML image and style tags (#6178)
- Added 9pt and 11pt to the list of font sizes in HTML editor
- Fix handling encoding of HTML tags in "inline" JSON output (#6207)
- Fix bug where some unix timestamps were not handled correctly by rcube_utils::anytodatetime() (#6212)
RELEASE 1.3.4
-------------
- Fix bug where contacts search could skip some records (#6130)
- Fix possible information leak - add more strict sql error check on user creation (#6125)
- Fix a couple of warnings on PHP 7.2 (#6098)
- Fix broken long filenames when using imap4d server - workaround server bug (#6048)
- Fix so temp_dir misconfiguration prints an error to the log (#6045)
- Fix untagged COPYUID responses handling - again (#5982)
- Fix PHP warning "idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated" with PHP 7.2 (#6075)
- Fix bug where Archive folder wasn't auto-created on login with create_default_folders=true
- Fix performance issue when parsing malformed and long Date header (#6087)
- Fix syntax error in mssql.initial.sql (#6097)
- Fix bug where contacts export by selection returned no more than 10 entries (#6103)
- Fix searching contacts by address in LDAP source (#6084)
- Fix X-Frame-Options:ALLOW-FROM support, remove custom click-jacking protection (#6057)
RELEASE 1.3.3
-------------
- Fix decoding of mailto: links with + character in HTML messages (#6020)
- Fix false reporting of failed upgrade in installto.sh (#6019)
- Fix file disclosure vulnerability caused by insufficient input validation [CVE-2017-16651] (#6026)
- Fix mangled non-ASCII characters in links in HTML messages (#6028)
RELEASE 1.3.2
-------------
- Fix bug where pink image was used instead of a thumbnail when image resize fails (#5933)
- Fix so files size/count limit is verified (client-side) also on drag-n-drop uploads (#5940)
- Fix invalid template loading on a message error in preview frame (#5941)
- Fix bug where HTML messages could have been rendered empty on some systems (#5957)
- Fix wording of "Mark previewed messages as read" to "Mark messages as read" (#5952)
- Enigma: Fix decryption of messages encoded with non-ascii charset (#5962)
- Fix missing cursor in HTML editor on mail reply (#5969)
- Fix (again) bug where image data URIs in css style were treated as evil/remote in mail preview (#5580)
- Fix bug where mail search could return empty result on servers without SORT capability (#5973)
- Fix bug where assets_path wasn't added to some watermark frames
- Fix so untagged COPYUID responses are also supported according to RFC6851 (#5982)
- Fix issue caused by non-default session.cookie_lifetime setting (#5961)
- Fix Edge encoding bug when pasting text into the HTML editor, update to TinyMCE 4.5.8 (#5885)
- Fix handling of unknown Content-Disposition type (#6002)
- Fix truncated folder name on messages list in multi-folder mode, for folders with non-ascii characters (#6004)
- Fix bug where removing the last subfolder did not hide toggle button on its parent record (#6007)
- Fix bug where ghost messages could be added to the list after fast delete (#5941)
RELEASE 1.3.1
-------------
- Add Preferences > Mailbox View > Main Options > Layout (#5829)
- Password: Fix compatibility with PHP 7+ in cpanel_webmail driver (#5820)
- Managesieve: Fix parsing dot-staffed lines in multiline text (#5838)
- Managesieve: Fix AM/PM suffix in vacation time selectors
- Managesieve: Fix bug where 'exists' operator was reset to 'contains' (#5899)
- Remove non-printable characters from filenames on download/display (#5880)
- Fix decoding non-ascii attachment names from TNEF attachments (#5646, #5799)
- Fix uninitialized string offset in rcube_utils::bin2ascii() and make sure rcube_utils::random_bytes() result has always requested length (#5788)
- Fix bug where HTML messages with @media styles could moddify style of page body (#5811)
- Fix style issue on selected and unfocused message that is part of a thread (#5798)
- Fix bug where a.button style from managesieve plugin could impact other elements (#5800)
- Fix position of selected icon for (Mailvelope) Encrypt button
- Fix fatal error when using DMY- or MDY-based date format in PostgreSQL (#5808)
- Fix bug where errors were not printed when using bin/update.sh (#5834)
- Fix PHP 7.2 warnings on count() use (#5845)
- Fix bug where Chrome could not upload the same file that was selected before (#5854)
- Fix duplicate messages on the list after deleting messages on the next to the last page (#5862)
- Fix bug where messages count was not updated after delete when imap_cache is set (#5872)
- Fix potential XSS vulnerability with malformed HTML message markup
- Fix sending message with "Too many public recipients" dialog buttons (#5924)
- Bring back double-click behavior on the message list which was removed in 1.3.0 (#5823)
- Enigma: Fix decrypting an encrypted+signed message when signature verification fails (#5914)
RELEASE 1.3.0
-------------
- Update to TinyMCE 4.5.7
- Fix bug where invalid recipients could be silently discarded (#5739)
- Fix conflict with _gid cookie of Google Analytics (#5748)
- Print error from CLI scripts when system/exec function is disabled (#5744)
- Fix bug where comment notation within style tag would cause the whole style to be ignored (#5747)
- Fix bug where it wasn't possible to scroll folders list in Edge (#5750)
- Fix folders list sorting on Windows - if php-intl is available (#5732)
- Fix addressbook searching by gender (#5757)
- Fix prevention from using % and * characters in folder name (#5762)
- Fix POST parameter reflection in default_charset selector (#5768)
- Enigma: Fix compatibility with assets_dir
- Managesieve: Skip redundant LISTSCRIPTS command
- Fix SQL syntax error on MariaDB 10.2 (#5774)
- Fix bug where zipdownload ignored files with the same name (#5777)
- Fix bug where it wasn't possible to set timezone to auto-detected value (#5782)
RELEASE 1.3-rc
--------------
- "Flattened" the larry theme: fresher look by removing shadows and gradients
- Support logging to php://stdout (#5721)
- Add support for DelSp=Yes in format=flowed messages (#5702)
- Update to jQuery 3.2.1
- Update to TinyMCE 4.5.6
- Plugin API: Call message_part_structure hook for sub-parts of multipart/alternative message (#5678)
- Enigma: Always use detached signatures (#5624)
- Enigma: Fix handling of messages with nested PGP encrypted parts (#5634)
- Minimize unwanted message loading in preview frame on drag (#5616)
- Fix failing database schema check in all engines except mysql (#5730)
- Fix autocomplete popup closing with click outside the input, don't handle Tab key as Enter (#5606)
- Fix jsdeps.json synchronization on update, warn about missing requirements of install-jsdeps.sh (#5598)
- Fix missing thread expand icon on search result in widescreen mode (#5613)
- Fix bug where image data URIs in css style were treated as evil/remote in mail preview (#5580)
- Fix bug where external content in src attribute of input/video tags was not secured (#5583)
- Fix PHP error on update of a contact with multiple email addresses when using PHP 7.1 (#5587)
- Fix bug where mail content frame couldn't be reset in some corner cases (#5608)
- Fix bug where some classic skin images were not displayed in IE/Edge (#5614)
- Fix bug where signature couldn't be added above the quote in Firefox 51 (#5628)
- Fix regression where groups with email address were resolved to its members' addresses
- Fix update of group name in the contacts list header on group rename (#5648)
- Add rewrite rule to disable access to /vendor/bin folder in .htaccess (#5630)
- Fix bug where it was too easy accidentally move a folder when using the subscription checkbox (#5655)
- Managesieve: Fix parser issue with empty lines between comments (#5657)
- Managesieve: Fix possible defect in handling \r\n in scripts (#5685)
- Fix/rephrase "unsaved changes" warning when cancelling a draft (#5610)
- Fix XSS issue in handling of a style tag inside of an svg element [CVE-2017-6820]
- Fix bug where settings/upload.inc could not be used by plugins (#5694)
- Fix regression in LDAP fuzzy search where it always used prefix search instead (#5713)
- Fix bug where namespace prefix could not be truncated on folders list if show_real_foldernames=true (#5695)
- Fix undesired effects when postgres database uses different timezone than PHP host (#5708)
- Installer: Fix DB schema initialization on MS SQL Server
- Fix bug where base_dn setting was ignored inside group_filters (#5720)
- Password: Fix security issue in virtualmin and sasl drivers [CVE-2017-8114]
RELEASE 1.3-beta
----------------
- Nicely handle contact deletion on contact edit (#5522)
- vcard_attachments: Add possibility to attach contact vCard to composed message (#4997)
- Preserve message internal/received date on import in mbox format (#5559)
- Zipdownload: Fix date format in mbox "From line"
- Possibility to display QR code for contacts data (#5030)
- Added identicon plugin
- Widescreen layout aka three column view (#5093)
- Unify automatic marking as \Seen in preview pane, full-page and extwin views (#5071)
- Disable double-click on the list when preview pane is on (#5199)
- Support hostname and hostname:port in force_https option (#5511)
- Support ALLOW-FROM in x_frame_options (#5122)
- Allow to omit a subject when sending an email (#5068)
- Warn about too many disclosed recipients in composed email [max_disclosed_recipients] (#5132)
- identity_select: Support Received header (#5085)
- Plugin API: Added get_compose_responses hook (#5457)
- Display error when trying to upload more files than specified in max_file_uploads (#5483)
- Add missing sql upgrade file for 'ip' column resize in session table (#5465)
- Do not show inline images of unsupported mimetype (#5463)
- Password: Added replacement variables support in password_pop_host (#5539)
- Password: Don't store passwords in temp files when using dovecotpw (#5531)
- Password: Added LDAP PPolicy driver (#5364)
- Password: Added cpanel_webmail driver (#5549)
- Password: Added possibility to nicely redirect from other plugins on password expiration (#5468)
- Implement separate action to mark all messages in a folder as \Seen (#5006)
- Implement marking as \Seen in all folders or in a folder and its subfolders (#5076)
- Archive: Don't reload messages list when it's not needed (#5225)
- Archive: Add option to automatically mark archived messages as \Seen (#5142)
- Improve randomness of password salts and random hashes (#5266)
- Password/cPanel: Add support for hash authentication and reseller accounts (#5252)
- Support host-specific imap_conn_options/smtp_conn_options/managesieve_conn_options (#5136)
- Center and scale images in attachment preview frame (#5421)
- Added max_message_size option enforced when attaching files to a composed message (#4993)
- Added Search button in quick search menus (#5312)
- Implement "one click" attachment/messages/photo upload (#5024)
- Squirrelmail_usercopy: Add option to define character set of data files
- Removed useless 'created' column from 'session' table (#5389)
- Dropped legacy browsers support (#5167)
- Removed legacy_browser plugin
- Removed hacks for IE < 10
- Update to jQuery 3.1.1 and jQuery-UI 1.12.0
- compile .min.js files with ECMASCRIPT5 option
- Require PHP >= 5.4
- Add possibility to preview and download attachments in mail compose (#5053)
- Add possibility to rename attachments in mail compose (#4996)
- Remove backward compatibility "layer" of bc.php (#4902)
- Support WEBP images in mail messages (#5362)
- Support MathML in HTML message preview (#5182)
- Rename Addressbook to Contacts (#5233)
- Remove PHP mail() support, smtp_server is required now (#5340)
- Display full message subject in onmouseover on truncated subject in mail view (#5346)
- Enigma: Support GnuPG 2.1 (#5313)
- Enigma: Support key generation for multiple identities (#5383)
- Enigma: Import keys from key-server(s) (#5286)
- Enigma: Search missing public keys on a key-server in mail compose (#5286)
- Enigma: Delete user keys when using deluser.sh script
- Enigma: Fix redundant list-secret-keys/list-public-keys calls on signing/encryption
- Enigma: Implement PGP encryption and signing in one go (#5302)
- Enigma: Display signature verification status for encrypted+signed messages (#5302)
- Display different attachment icon on encrypted messages
- Display different confirmation text when moving messages to Trash (#5220)
- Indicate that a collapsed thread has flagged children (#5013)
- Implemented message/rfc822 attachment preview
- Update to jsTimezoneDetect 1.0.6
- Managesieve: Add (optional) RAW script editor (#5414)
- Managesieve: Add option to automatically set vacation :from address (#5428)
- Managesieve: Support 'string' test from variables extension [RFC 5229] (#5248)
- Managesieve: Support 'duplicate' extension [RFC 7352]
- Managesieve: Unhide advanced rule controls if there are inputs with errors
- Managesieve: Display warning message when filter form contains errors
- Control search engine crawlers via X-Robots-Tag header instead of <meta> and robots.txt (#5098)
- Fixed redundancy in sql caching system and compatibility with Galera Cluster (#5439)
- Removed redundant 'created' column from cache and cache_shared tables
- Removed use of redundant data records
- Added missing primary keys (dictionary, cache, cache_shared tables)
- Fix so templating system does not mess with external (e.g. email) content (#5499)
- Fix redundant keep-alive/refresh after session error on compose page (#5500)
- Managesieve: Fix handling of scripts with nested rules (#5540)
- Fix variable substitution in ldap host for some use-cases, e.g. new_user_identity (#5544)
- Enigma: Fix PHP fatal error when decrypting a message with invalid signature (#5555)
- Fix adding images to new identity signatures
- Fix rsync error handling in installto.sh script (#5562)
- Fix some advanced search issues with multiple addressbooks (#5572)
- Fix so group/addressbook selection is retained on page refresh
RELEASE 1.2.3
-------------
- Searching in both contacts and groups when LDAP addressbook with group_filters option is used
- Fix vulnerability in handling of mail()'s 5th argument
- Fix To: header encoding in mail sent with mail() method (#5475)
- Fix flickering of header topline in min-mode (#5426)
- Fix bug where folders list would scroll to top when clicking on subscription checkbox (#5447)
- Fix decoding of GB2312/GBK text when iconv is not installed (#5448)
- Fix regression where creation of default folders wasn't functioning without prefix (#5460)
- Enigma: Fix bug where last records on keys list were hidden (#5461)
- Enigma: Fix key search with keyword containing non-ascii characters (#5459)
- Fix bug where deleting folders with subfolders could fail in some cases (#5466)
- Fix bug where IMAP password could be exposed via error message (#5472)
- Fix bug where it wasn't possible to store more that 2MB objects in memcache/apc,
Added memcache_max_allowed_packet and apc_max_allowed_packet settings (#5452)
- Fix "Illegal string offset" warning in rcube::log_bug() on PHP 7.1 (#5508)
- Fix storing "empty" values in rcube_cache/rcube_cache_shared (#5519)
- Fix missing content check when image resize fails on attachment thumbnail generation (#5485)
- Fix displaying attached images with wrong Content-Type specified (#5527)
RELEASE 1.2.2
-------------
- Enigma: Add possibility to configure gpg-agent binary location (enigma_pgp_agent)
- Enigma: Fix signature verification with some IMAP servers, e.g. Gmail, DBMail (#5371)
- Enigma: Make recipient key searches case-insensitive (#5434)
- Fix regression in resizing JPEG images with Imagick (#5376)
- Managesieve: Fix parsing of vacation date-time with non-default date_format (#5372)
- Use SymLinksIfOwnerMatch in .htaccess instead of FollowSymLinks disabled on some hosts for security reasons (#5370)
- Wash position:fixed style in HTML mail for better security (#5264)
- Fix bug where memcache_debug didn't work for session operations
- Fix bug where Message-ID domain part was tied to username instead of current identity (#5385)
- Fix bug where blocked.gif couldn't be attached to reply/forward with insecure content
- Fix E_DEPRECATED warning when using Auth_SASL::factory() (#5401)
- Fix bug where names of downloaded files could be malformed when derived from the message subject (#5404)
- Fix so "All" messages selection is resetted on search reset (#5413)
- Fix bug where folder creation could fail if personal namespace contained more than one entry (#5403)
- Fix error causing empty INBOX listing in Firefox when using an URL with user:password specified (#5400)
- Fix PHP warning when handling shared namespace with empty prefix (#5420)
- Fix so folders list is scrolled to the selected folder on page load (#5424)
- Fix so when moving to Trash we make sure the folder exists (#5192)
- Fix displaying size of attachments with zero size
- Fix so "Action disabled" error uses more appropriate 404 code (#5440)
RELEASE 1.2.1
-------------
- Update TinyMCE to version 4.3.13 (#5309)
- Fix bug where errors could have been not logged when per_user_logging=true
- Fix bug where message list columns could be in wrong order after column drag-n-drop and list sorting
- Fix so minified publickey.js (with cache-buster) is used when available (#5254)
- Fix (replace) application/x-tar file extension test as it might not exist in nginx config (#5253)
- Fix PHP warning when password_hosts is set, but is not an array (#5260)
- Fix redundant keep-alive requests when session_lifetime is greater than ~20000 (#5273)
- Fix so subfolders of INBOX can be set as Archive (#5274)
- Fix bug where multi-folder search could choose a wrong folder in "this and subfolders" scope (#5282)
- Fix bug where multi-folder search didn't work for unsubscribed INBOX (#5259)
- Fix bug where "no body" alert could be displayed when sending mailvelope email
- Enigma: Fix keys import from inside of an encrypted message (#5285)
- Enigma: Fix malformed signed messages with force_7bit=true (#5292)
- Enigma: Add possibility to configure gpg binary location (enigma_pgp_binary)
- Enigma: Add possibility to export private keys (#5321)
- Fix searching by email address in contacts with multiple addresses (#5291)
- Fix handling of --delete argument in moduserprefs.sh script (#5296)
- Workaround PHP issue by calling closelog() on script shutdown when using log_driver=syslog (#5289)
- Fix so upgrade script makes sure program/lib directory does not contain old libraries (#5287)
- Fix subscription checkbox state on error in folder subscribe/unsubscribe action (#5243)
- Fix bug where microsecond format in logged date didn't work in some cases
- Fix conflict in new_user_dialog and password_force_new_user settings (#5275)
- Don't create multipart/alternative messages with empty text/plain part (#5283)
- Use contact_search_name format in popup on results in compose contacts search
- Fix handling of 'mailto' and 'error' arguments in message_before_send hook (#5347)
- Fix missing localization of HTML editor when assets_dir != INSTALL_PATH
- Fix handling of blockquote tags with mixed case on html2text conversion (#5363)
- Fix javascript errors in IE on page with iframe that points to another domain
RELEASE 1.2.0
-------------
- Enigma: Added enigma_debug option
- Fix message list multi-select/deselect issue (#5219)
- Fix bug where getting HTML editor content could steal focus from other form controls (#5223)
- Fix bug where contact search menu fields where always unchecked in Larry skin
- Fix autoloading of 'html' class
- Fix bug where Encrypt button appears when switching editor to HTML (#5235)
- Fix XSS issue in href attribute on area tag (#5240)
RELEASE 1.2-rc
--------------
- Managesieve: Refactored script parser to be 100x faster
- Enigma: added option to force users to use signing/encryption
- Enigma: Added option to attach public keys to sent mail (#5152)
- Enigma: Handle messages with text before an encrypted block (#5149)
- Enigma: Handle encrypted/signed content inside message/rfc822 attachments
- Enigma: Fix missing html/plain switch on multipart/signed messages (#4963)
- Enigma: Disable format=flowed for signed plain text messages (#4960)
- Enigma: Fix handling of encrypted + signed messages (#4950)
- Enigma: Fix invalid boundary use in signed messages structure
- Enable use of TLSv1.1 and TLSv1.2 for IMAP (#4955)
- Save copy of original .htaccess file when using installto.sh script (#4947)
- Fix regression where some message attachments could be missing on edit/forward (#4939)
- Fix regression in displaying contents of message/rfc822 parts (#4937)
- Fix handling of message/rfc822 attachments on replies and forwards (#4938)
- Fix PDF support detection in Firefox > 19 (#4941)
- Fix path traversal vulnerability in setting a skin [CVE-2015-8770] (#4945)
- Fix so drag-n-drop of text (e.g. recipient addresses) on compose page actually works (#4944)
- Fix .htaccess rewrite rules to not block .well-known URIs (#4943)
- Fix mail view scaling on iOS (#4915)
- Fix PHP7 warning "session_start(): Session callback expects true/false return value" (#4948)
- Fix XSS issue in SVG images handling [CVE-2015-8864, CVE-2016-4068] (#4949)
- Fix missing language name in "Add to Dictionary" request in HTML mode (#4951)
- Fix (again) security issue in DBMail driver of password plugin [CVE-2015-2181] (#4958)
- Fix bug where Archive/Junk buttons were not active after page jump with select=all mode (#4961)
- Fix bug in long recipients list parsing for cases where recipient name contained @-char (#4964)
- Plugin API: Added addressbook_export hook
- Fix additional_message_headers plugin compatibility with Mail_Mime >= 1.9 (#4966)
- Hide DSN option in Preferences when smtp_server is not used (#4967)
- Fix handling of body parameter in mail compose request
- Protect download urls against CSRF using unique request tokens [CVE-2016-4069] (#4957)
- newmail_notifier: Refactor desktop notifications
- Fix so contactlist_fields option can be set via config file
- Fix so SPECIAL-USE assignments are forced only until user sets special folders (#4782)
- Fix performance in reverting order of THREAD result
- Fix converting mail addresses with @www. into mailto links (#5197)
RELEASE 1.2-beta
----------------
- Update TinyMCE to version 4.2
- Added support for Redis session handler
- Removed some deprecated methods: https://github.com/roundcube/roundcubemail/commit/454b0b1c
- Remove backward compatibility "layer" of bc.php (#4902)
- Add possibility to define date format in write operations for ldap attributes (#3956)
- Display attachment size in compose (#1329)
- Added possibility to drag-n-drop attachments from mail preview to compose window
- Implemented mail messages searching with predefined date interval
- PGP encryption support via Mailvelope integration
- PGP encryption support via Enigma plugin
- PHP7 compatibility fixes (#4836)
- Security: Added brute-force attack prevention via login rate limit (#4922)
- Security: Added options to validate username/password on logon (#4884)
- Security: Improve randomness of security tokens (#4899)
- Security: Use random security tokens instead of hashes based on encryption key (#4829)
- Security: Improved encrypt/decrypt methods with option to choose the cipher_method (#4492)
- Make optional adding of standard signature separator - sig_separator (#3276)
- Optimize folder_size() on Cyrus IMAP by using special folder annotation (#4894)
- Make optional hidding of folders with name starting with a dot - imap_skip_hidden_folders (#4870)
- Add option to enable HTML editor always, except when replying to plain text messages (#4352)
- Emoticons: Added option to switch on/off emoticons in compose editor (#2076)
- Emoticons: Added option to switch on/off emoticons in plain text messages
- Emoticons: All emoticons-related functionality is handled by the plugin now
- Installer: Add button to save generated config file in system temp directory (#3553)
- Remove common subject prefixes Re:, Re[x]:, Re-x: on reply (#4882)
- Added GSSAPI/Kerberos authentication plugin - krb_authentication
- Password: Allow temporarily disabling the plugin functionality with a notice
- Require Mbstring and OpenSSL extensions (#5166)
- Add --config and --type options to moduserprefs.sh script (#4651)
- Implemented memcache_debug and apc_debug options
- Installer: Remove system() function use (#4695)
- Password plugin: Added 'kpasswd' driver by Peter Allgeyer
- Add initdb.sh to create database from initial.sql script with prefix support (#4722)
- Plugin API: Added disabled_plugins an disabled_buttons options in html_editor hook
- Plugin API: Added html2text hook
- Plugin API: Added message_part_body hook
- Plugin API: Added message_ready hook
- Plugin API: Add special onload() method to execute plugin actions before startup (session and GUI initialization)
- Implemented UI element to jump to specified page of the messages list (#1677)
- Fix searching of contacts to allow remote images for known senders (#4886)
- Fix bug where clicking date column with 'arrival' sorting would switch to sorting by 'date' (#4690)
- Fix bug where message content could overlap attachments list in Larry skin (#4876)
- Fix so microseconds macro (u) in log_date_format works (#4855)
- Fix so unrecognized TNEF attachments are displayed on the list of attachments (#5138)
- Fix so database_attachments::cleanup() does not remove attachments from other sessions (#4907)
- Fix responses list update issue after response name change (#4917)
- Fix bug where message preview was unintentionally reset on check-recent action (#4921)
- Fix bug where HTML messages with invalid/excessive css styles couldn't be displayed (#4905)
- Fix redundant blank lines when using HTML and top posting (#4927)
- Fix redundant blank lines on start of text after html to text conversion (#4928)
- Fix HTML sanitizer to skip <!-- node type X --> in output (#4932)
- Fix invalid LDAP query in ACL user autocompletion (#4934)
RELEASE 1.1.3
-------------
- Fix closing of nested menus (#4854)
- Fix so E_DEPRECATED errors from PEAR libs are ignored by error_reporting change (#4770)
- Fix compatibility with PHP 5.3 in rcube_ldap class (#4842)
- Get rid of Mail_mimeDecode package dependency (#4836)
- Fix "Importing..." message does not hide on error (#4840)
- Fix Compose action in addressbook for results from multiple addressbooks (#4834)
- Fix bug where some messages in multi-folder search couldn't be viewed/printed/downloaded (#4843)
- Fix unintentional messages list page change on page switch in compose addressbook (#4844)
- Fix race-condition in saving user preferences and loading plugin config (#4845)
- Fix so plain text signature field uses monospace font (#4848)
- Fix so links with href == content aren't added to links list on html to text conversion (#4847)
- Fix handling of non-break spaces in html to text conversion (#4849)
- Fix self-reply detection issues (#4852)
- Fix multi-folder search result sorting by arrival date (#4858)
- Fix so *-request@ addresses in Sender: header are also ignored on reply-all (#4860)
- Update to TinyMCE 4.1.10 (#5164)
- Fix draft removal after a message is sent and storing sent message is disabled (#4869)
- Fix so imap folder attribute comparisons are case-insensitive (#4868)
- Fix bug where new messages weren't added to the list in search mode
- Fix wrong positioning of message list header on page scroll in Webkit browsers (#4646)
- Fix some javascript errors in rare situations (#4853)
- Fix error when using back button after sending an email (#4628)
- Fix removing signature when switching to identity with an empty sig in HTML mode (#4872)
- Disable links list generation on html-to-text conversion of identities or composed message (#4850)
- Fix "washing" of style elements wrapped into many lines
- Fix so input field (e.g. search box) does not loose focus on list load (#4862)
- Fix so css of one html part does not apply to other text parts on message display (#4887)
- Fix XSS issue in drag-n-drop file uploads [CVE-2015-8105] (#4900)
- Fix handling of plus character in mailto: links (#4891)
- Fix so adding CC/BCC recipients from the sidebar unhides compose form fields in Classic skin (#4874)
- Fix so gc.sh script removes also expired sessions from sql database (#4893)
- Fix support for Mozilla-based browsers, e.g. Pale Moon (#4895)
- Fix various issues with Turkish (and similar) locales (#4896)
- Fix so In-Reply-To header is set also for MDN receipts (#4897)
- Fix missing HTTP_X_FORWARDED_FOR address in generated Received header
- Fix issue where Content-Length of some attachments could be set to wrong value causing browser errors (#4877)
RELEASE 1.1.2
-------------
- Add new plugin hook 'identity_create_after' providing the ID of the inserted identity (#4807)
- Add option to place signature at bottom of the quoted text even in top-posting mode [sig_below]
- Fix handling of %-encoded entities in mailto: URLs (#4799)
- Fix zipped messages downloads after selecting all messages in a folder (#4797)
- Fix vpopmaild driver of password plugin
- Fix PHP warning: Non-static method PEAR::setErrorHandling() should not be called statically (#4798)
- Fix tables listing routine on mysql and postgres so it skips system or other database tables and views (#4796)
- Fix message list header in classic skin on window resize in Internet Explorer (#4732)
- Fix so text/calendar parts are listed as attachments even if not marked as such (#4795)
- Fix lack of signature separator for plain text signatures in html mode (#4802)
- Fix font artifact in Google Chrome on Windows (#4803)
- Fix bug where forced extwin page reload could exit from the extwin mode (#4801)
- Fix bug where some unrelated attachments in multipart/related message were not listed (#4805)
- Fix mouseup event handling when dragging a list record (#4808)
- Fix bug where preview_pane setting wasn't always saved into user preferences (#4809)
- Fix bug where messages count was not updated after message move/delete with skip_deleted=false (#4814)
- Fix security issue in contact photo handling (#4817)
- Fix possible memcache/apc cache data consistency issues (#4820)
- Fix bug where imap_conn_options were ignored in IMAP connection test (#4822)
- Fix bug where some files could have "executable" extension when stored in temp folder (#4815)
- Fix attached file path unsetting in database_attachments plugin (#4823)
- Fix issues when using moduserprefs.sh without --user argument (#4825)
- Fix potential info disclosure issue by protecting directory access (#4816)
- Fix blank image in html_signature when saving identity changes (#4833)
- Installer: Use openssl_random_pseudo_bytes() (if available) to generate des_key (#4827)
- Fix XSS vulnerability in _mbox argument handling (#4837)
RELEASE 1.1.1
-------------
- ACL: Allow other plugins to adjust the list of permissions and groups to edit
- Add possibility to print contact information (of a single contact)
- Add possibility to configure max_allowed_packet value for all database engines (#4772)
- Improved handling of storage errors after message is sent
- Update to TinyMCE 4.1.9
- Unified request* event arguments handling, added support for _unlock and _action parameters
- Security: Generate random hash for the per-user local storage prefix (#4768)
- Fix refreshing of drafts list when sending a message which was saved in meantime (#4745)
- Fix saving/sending emoticon images when assets_dir is set
- Fix PHP fatal error when visiting Vacation interface and there's no sieve script yet (#4778)
- Fix setting max packet size for DB caches and check packet size also in shared cache
- Fix needless security warning on BMP attachments display (#4771)
- Fix handling of some improper constructs in format=flowed text as per the RFC3676[4.5] (#4773)
- Fix performance of rcube_db_mysql::get_variable()
- Fix missing or not up-to-date CATEGORIES entry in vCard export (#4766)
- Fix fatal errors on systems without mbstring extension or mb_regex_encoding() function (#4769)
- Fix cursor position on reply below the quote in HTML mode (#4759)
- Fix so "over quota" errors are displayed also in message compose page
- Fix duplicate entries suppression in autocomplete result (#4776)
- Fix "Non-static method PEAR::isError() should not be called statically" errors (#4770)
- Fix parsing invalid HTML messages with BOM after <!DOCTYPE> (#4777)
- Fix duplicate entry on timezones list in rcube_config::timezone_name_from_abbr() (#4779)
- Fix so localized folder name is displayed in multi-folder search result (#4750)
- Fix javascript error after creating a folder which is a subfolder of another one (#4781)
- Fix bug where subject of sent/saved message was removed if mbstring wasn't installed (#4780)
- Fix missing vcard_attachment icon on messages list (#4783)
- Fix storing signatures with big images in MySQL database (#4785)
- Fix Opera browser detection in javascript (#4786)
- Fix so search filter, scope and fields are reset on folder change
- Fix rows count when messages search fails (#4760)
- Fix bug where spellchecking in HTML editor do not work after switching editor type more than once (#4789)
- Fix bug where TinyMCE area height was too small on slow network connection (#4788)
- Fix backtick character handling in sql queries (#4790)
- Fix redirect URL for attachments loaded in an iframe when behind a proxy (#4724)
- Fix menu container references to point to the actual <ul> element (#4791)
- Fix javascripts errors in IE8 - lack of Event.which, focusing a hidden element (#4793)
RELEASE 1.1.0
-------------
- Make SMTP error log more verbose - include server response and error code
- Fix download options menu (added by zipdownload plugin) in classic skin (#4740)
- Fix blocked.gif image usage with assets_dir set
- Fix bug where max_group_members was ignored when adding a new contact (#4733)
- Hide MDN and DSN options in compose if disabled by admin (#4735)
- Fix checks based on window.ActiveXObject in IE > 10
- Fix XSS issue in style attribute handling [CVE-2015-1433] (#4739)
- Fix bug where Drafts list wasn't updated on draft-save action in new window (#4737)
- Fix so "set as default" option is hidden if identities_level > 1 (#4738)
- Fix bug where search was reset after returning from compose visited for reply
- Fix javascript error in "IE 8.0/Tablet PC" browser (#4730)
- Fix bug where Reply-To address was ignored on reply to messages sent by self (#4742)
- Fix bug where empty fieldmap config entries caused empty results of ldap search (#4741)
- Fix bug where drafts list wasn't refreshed after draft message was sent from another window (#4745)
- Fix keyboard navigation and css in datepicker widget across many Firefox versions
- Fix false warning when opening attached text/plain files (#4748)
- Fix bug where signature could have been inserted twice after plain-to-html switch (#4746)
- Fix security issue in DBMail driver of password plugin (#4757)
- Enable FollowSymLinks option in .htaccess file which is required by rewrite rules (#4754)
- Fix so JSON.parse() errors on localStorage items are ignored (#4752)
RELEASE 1.1-rc
--------------
- Update jQuery to version 2.1.3
- Allow to override any config option through env variables
- Improve system security by using optional special URL with security token - use_secure_urls
- Allow to define separate server/path for image/js/css files - assets_url/assets_dir
- Sync vendor folder if exists in source package (#4700)
- Avoid useless reloading list when resetting search with active filter (#4654)
- Fix invalid folder selection if clicked while busy (#4709)
- Fix import of multiple contact email addresses from Outlook-csv format (#4714)
- Fix drag-n-drop to folders expanded while dragging (#4708)
- Fix import of multiple contact groups from Google-csv format (#4710)
- Fix import of contacts with multiple email addresses from Google-csv format (#4719)
- Fix bugs where CSRF attacks were still possible on some requests [CVE-2014-9587]
- Fix some rcube_utils::anytodatetime() corner cases with timezone mismatches (#4712)
- Improve move-to and contact-export button in classic skin (#4713)
- Fix wrong icon for download button in classic skin
- Fix bug where sent message was saved in Sent folder even if disabled by user (#4729)
RELEASE 1.1-beta
----------------
- Fix skin path handling in plugin context (#4111)
- Prevent memory exhaustion on image resizing with GD on Windows (#4580)
- Add plugin hook for database table name lookups as requested in #4538
- Added Oracle database support
- Support contacts import in GMail CSV format
- Added namespace filter in Folder Manager
- Added folder searching in Folder Manager
- Fix restoring draft messages from localStorage if editor mode differs (#4631)
- Added config option/user preference to disable saving messages in localStorage (#4606)
- Added config option 'imap_log_session' to enable Roundcube <-> IMAP session ID logging
- Added config option 'log_session_id' to control the length of the session identifier in logs
- Implemented 'storage_connected' API hook after successful IMAP login (#4638)
- Intergrate Net_LDAP3 and rcube_ldap_generic classes
- Add option (disabled_actions) to disable UI elements/actions (#4478)
- Support password encryption using openssl extension (#4614)
- Create/rename groups in UI dialogs (#4592)
- Added 'contact_search_name' option to define autocompletion entry format
- Display quota information for current folder not INBOX only (#3442)
- Support images in HTML signatures (#3917)
- Display full quota information in popup (#2103, #2746)
- Mail compose: Selecting contact inserts recipient to previously focused input - to/cc/bcc accordingly (#4487)
- Close "no subject" prompt with Enter key (#4463)
- Password: Add option to force new users to change their password (#2963)
- Improve support for screen readers and assistive technology using WCAG 2.0 and WAI ARIA standards
- Enable basic keyboard navigation throughout the UI (#3333)
- Select/scroll to previously selected message when returning from message page (#4146)
- Display a warning if popup window was blocked (#4472)
- Remove (was: ...) from message subject on reply (#4359)
- Update to TinyMCE 4.1 (#4168)
- Enable autolink plugin in TinyMCE (#4029)
- Support image operations with Imagick extension (#4498)
- Support upload progress with session.upload_progress and PECL uploadprogress module (#3934)
- Make identity name field optional (#4435)
- Utility script to remove user records from the local database
- Plugin API: Added message_saved hook (#4503)
- Plugin API: Added imap_search_before hook
- Support messages import from zip archives
- Zipdownload: Added mbox format support (#2354)
- Drop support for IE6, move IE7/IE8 support to legacy_browser plugin
- Update to jQuery-2.1.1
- Search across multiple folders (#1676)
- Improve UI integration of ACL settings
- Drop support for PHP < 5.3.7
- Set In-Reply-To and References for forwarded messages (#4465)
- Removed redundant default_folders config option (#4500)
- Implemented IMAP SPECIAL-USE extension support [RFC6154] (#3326)
- Optimize some framed pages content for better performance (#4517)
- Improve text messages display and conversion to HTML (#4091)
- Don't remove links when html signature is converted to text (#4473)
- Fix page title when using search filter (#4636)
- Fix mbox files import
- Fix some character sets detection (#4694)
- Fix so attachment charset is set in headers of forward/draft message (#4676)
- Fix bug where wrong charset could be used for text attachment preview page (#4674)
RELEASE 1.0.5
-------------
- Fix wrong icon for download button in classic skin
- Fix checks based on window.ActiveXObject in IE > 10
- Fix XSS issue in style attribute handling (#4739)
- Fix bug where Drafts list wasn't updated on draft-save action in new window (#4737)
- Fix so "set as default" option is hidden if identities_level > 1 (#4738)
- Fix javascript error in "IE 8.0/Tablet PC" browser (#4730)
- Fix bug where empty fieldmap config entries caused empty results of ldap search (#4741)
- Fix bug where sent message was saved in Sent folder even if disabled by user (#4729)
RELEASE 1.0.4
-------------
- Disable TinyMCE contextmenu plugin as there are more cons than pros in using it (#4684)
- Fix bug where show_real_foldernames setting wasn't honored on compose page (#4705)
- Fix issue where Archive folder wasn't protected in Folder Manager (#4706)
- Fix compatibility with PHP 5.2. in rcube_imap_generic (#4682)
- Fix setting flags on servers with no PERMANENTFLAGS response (#4667)
- Fix regression in SHAA password generation in ldap driver of password plugin (#4670)
- Fix displaying of HTML messages with absolutely positioned elements in Larry skin (#4672)
- Fix font style display issue in HTML messages with styled <span> elements (#4671)
- Fix download of attachments that are part of TNEF message (#4668)
- Fix handling of uuencoded messages if messages_cache is enabled (#4675)
- Fix handling of base64-encoded attachments with extra spaces (#4678)
- Fix handling of UNKNOWN-CTE response, try do decode content client-side (#4650)
- Fix bug where creating subfolders in shared folders wasn't possible without ACL extension (#4680)
- Fix reply scrolling issue with text mode and start message below the quote (#4681)
- Fix possible issues in skin/skin_path config handling (#4689)
- Fix lack of delimiter for recipient addresses in smtp_log (#4703)
- Fix generation of Blowfish-based password hashes (#4721)
- Fix bugs where CSRF attacks were still possible on some requests [CVE-2014-9587]
RELEASE 1.0.3
-------------
- Initialize HTML editor before restoring a message from localStorage (#4631)
- Add 'sig_max_lines' config option to default config file (#5162)
- Add config option to specify IMAP connection socket parameters - imap_conn_options (#4589)
- Add option to set default message list mode - default_list_mode (#3157)
- Enable contextmenu plugin for TinyMCE editor (#3062)
- Fix insert-signature command in external compose window if opened from inline compose screen (#4663)
- Fix some mime-type to extension mapping checks in Installer (#4610)
- Fix errors when using localStorage in Safari's private browsing mode (#4619)
- Fix bug where $Forwarded flag was being set even if server didn't support it (#4621)
- Fix various iCloud vCard issues, added fallback for external photos (#4617)
- Fix invalid Content-Type header when send_format_flowed=false (#4616)
- Fix errors when adding/updating contacts in active search (#4630)
- Fix incorrect thumbnail rotation with GD and exif orientation data (#4641)
- Fix contacts list update after adding/deleting/moving a contact (#4640, #4644)
- Fix handling of email addresses with quoted domain part (#4647)
- Fix comm_path update on task switch (#4648)
- Fix error in MSSQL update script 2013061000.sql (#4658)
- Fix validation of email addresses with IDNA domains (#4661)
RELEASE 1.0.2
-------------
- Fix storing unsaved drafts in localStorage (#4529)
- Add configurable LDAP_OPT_DEREF option (#4546)
- Fix so when switching editor mode original version of signature is used (#4032)
- Fix unintentional draft autosave request if autosave is disabled (#4550)
- Fix malformed References: header in send/saved mail (#4552)
- Fix handling unicode characters in links (#4555)
- Fix incorrect handling of HTML comments in messages sanitization code (#4558)
- Fix so current page is reset on list-mode change (#4561)
- Fix so responses menu hides on click in classic skin (#4566)
- Fix unintentional line-height style modification in HTML messages (#4567)
- Fix broken normalize_string(), add support for ISO-8859-2 (#4568)
- Support csv contacts import in German localization (#4570)
- Fix so message list and counters are updated when a message is opened in new window (#4569)
- Fix malformed recipient name when composing a message by clicking on mailto link (#4583)
- Fix list reload after sending message in another window (#4576)
- Fix so address format errors are ignored when saving a draft (#4594)
- Fix incorrect label translation in return receipt (#4598)
- Fix security issue in delete-response action - allow only ajax request
- Fix Delete button state after deleting identity/response (#4603)
- Fix bug where contacts with no email address were listed on compose addressbook (#4602)
- Fix images import from various vCard formats (#4604)
- Fix sorting messages by size on servers without SORT capability (#4608)
RELEASE 1.0.1
-------------
- Support 'error' and 'body_file' return attribs in 'message_before_send' hook (#4467)
- Apply user-specific replacements to group's base_dn property (#4512)
- Fix missing email address when importing contacts from outlook csv (#4535)
- Fix bug where "With attachment" option in search filter wasn't selected after return from mail view (#4508)
- Fix "washing" of unicoded style attributes (#4510)
- Fix unintentional redirect from compose page in Webkit browsers (#4516)
- Fix messages index cache update under some conditions (e.g. proxy) (#4505)
- Fix lack of translation of special folders in some configurations (#4520)
- Fix XSS issue in plain text spellchecker (#4524)
- Fix invalid page title for some folders (1489804)
- Fix redundant alert message on over-size uploads (#4528)
- Fix next message display after removing a message (#4521)
- Fix missing Mail-Followup-To header in sent mail (#4534)
- Fix error when spell-checking an empty text (#4536)
- Avoid popupmenus being closed when scrollbar is clicked (#4537)
- Add proxy_whitelist configuration option (#4496)
- Fix identities_level=4 handling in new_user_dialog plugin (#4540)
- Fix various db_prefix issues (#4539)
- Fix too small length of users.preferences column data type on MySQL
- Fix redundant warning when switching from html to text in empty editor (#4530)
- Fix invalid host validation on login (#4541)
- Fix IMAP connection test in installer so it is aware of imap_auth_type (#4502)
RELEASE 1.0.0
-------------
- Added toolbar button to move message in message view
- Fix style of disabled protocol handler link on IE (#4460)
- Fix message import dialog when no file is selected (#4488)
- Fix opening compose screen in new window after saving as draft (#4479)
- Fix directories check in Installer on Windows (#4462)
- Fix issue when default_addressbook option is set to integer value (#4379)
- Fix Opera > 15 detection (#4455)
- Fix security issue in DomainFactory driver of Password plugin
- Fix invalid X-Draft-Info on forwarded message draft (#4464)
- Fix regression in handling of 'attachments' result in message_compose hook (#4474)
- Fix issue where msgexport.sh printed the message to STDOUT instead of a file (#4476)
- Fix fatal error in database_attachments plugin under some conditions (#4495)
RELEASE 1.0-rc
--------------
- Small CSS fix with message notice boxes in Larry skin (#4429)
- Include groups in contacts search on mail compose (#4186)
- Add mime-type mapping for .7z files (#4436)
- Invoke update scripts with php to circumvent execution restrictions (#4330)
- Fix drag & drop message/contact moving on touch device (#4395)
- Fix canned responses in HTML mode (#4446)
- Check/create default folders on every login not only the first (#4391)
- Update to jQuery-1.11.0 and jQuery-UI-1.9.2
- Support SMTP socket context options via new config option 'smtp_conn_options'
- Fix compatibility with PHP 5.2 in html.php file (#4438)
- Remove expand/collapse with plus/minus keys (on numeric keypad) (#4437)
- Fix issue where filesystem path was added to all-attachments (zip) file (#4433)
- Fix case-sensitivity of email addresses handling on compose (#1899)
- Don't alter Message-ID of a draft when sending (#4381)
- Fix issue where deprecated syntax for HTML lists was not handled properly (#3975)
- Display different icons when Trash folder is empty or full (#2108)
- Remember last position of more headers switch (#3660)
- Fix so message flags modified by another client are applied on the list on refresh (#1639)
- Fix broken text/* attachments when forwarding/editing a message (#4393)
- Improved minified files handling, added css minification (#3041)
- Fix handling of X-Forwarded-For header with multiple addresses (#4424)
- Fix border issue on folders list in classic skin (#4419)
- Implemented menu actions to copy/move messages, added folder-selector widget (#863)
- Fix security rules in .htaccess preventing access to base URL without the ending slash (#4422)
- Fix regression where only first new folder was placed in correct place on the list (#4418)
- Fix issue where children of selected and collapsed thread were skipped on various actions (#4410)
- Fix issue where groups were not deleted when "Replace entire addressbook" option on contacts import was used (#4388)
- Fix unreliable mimetype tests in Installer (#4408)
- Fix performance of listing writeable folders (#4406)
RELEASE 1.0-beta
----------------
- Fix handling of invalid closing tags in HTML messages (#4403)
- Set real content-type for file downloads (#4400)
- Update TinyMCE to version 3.5.10 (#4401)
- Fix keyboard navigation in list widgets (#4367)
- Allow plugins to grab the reference of opened windows (#4383)
- Larry skin: Improved status message display for better visibility (#4115)
- Fix Internet Explorer 11 detection (#4397)
- Fix date column width to fit the widest possible date format (#4354)
- Move certain user preference options to a collapsed "advanced" block (#4015)
- Add file type icons for Powerpoint and Open Office presentations (#4269)
- Fix operations on folders with trailing spaces in name (#4387)
- Improve identity selection based on From: header (#4360)
- Fix issue where mails with inline images of the same name contained only the first image multiple times (#4378)
- Use left/right arrow keys to collapse/expand thread and spacebar to select a row, change Ctrl key behavior (#4367)
- Fix an issue where using arrow keys to go up a list can result in selected message being under headers (#4375)
- Fix an issue where Home/End keys don't focus list row properly, don't scrollTo properly (#4370)
- Add an option to disable smart Reply-List behaviour - reply_all_mode (#3953)
- Fix an issue where pressing minus key on contacts list was hiding list records (#4368)
- Fix an issue where shift + arrow-up key wasn't selecting all messages in collapsed thread (#4371)
- Added icon for priority column in messages list header (#4275)
- New feature "Canned Responses" to save and recall boilerplate text snippets
- Fix HTML part detection when encapsulated inside multipart/signed (#4357)
- Add spellchecker backend for the After the Deadline service
- Replace markdown-style [1] link indexes in plain text email bodies
- Improved mailto: link arguments handling (#4351)
- Use DOMDocument LIBXML_PARSEHUGE and LIBXML_COMPACT options if possible (#4316)
- Support HTTP_HOST, SERVER_NAME and SERVER_ADDR values in include_host_config feature
- Make default font size for HTML messages configurable (request #118)
- Fix XSS issue in addressbook group name field [CVE-2013-5646] (#4337)
- After message is sent refresh messages list of replied message folder (#4282)
- Add option force specified domain in user login - username_domain_forced (#4290)
- Add option to import Vcards with group assignments
- Save groups membership in Vcard export (#3801)
- Workaround broken PHP function timezone_name_from_abbr (#4289)
- Make cached message size limit configurable - messages_cache_threshold (#4326)
- Log also failed logins to userlogins log
- Add temp_dir_ttl configuration option (#4318)
- Allow setting INBOX as Sent folder (#4264)
- Fix replacement variables in user-specific base_dn in some LDAP requests (#4299)
- Fix image scaling issues when image has only one dimension smaller than the limit (#4296)
- Fix issue where uploaded photo was lost when contact form did not validate (#4296)
- Move identity selection based on non-standard headers into (new) identity_select plugin (#3835)
- Fix downloading binary files with (wrong) text/* content-type (#4292)
- Respect HTTP_X_FORWARDED_FOR and HTTP_X_REAL_IP variables for session IP check
- Simplified configuration by merging it into one file + defaults (#3156)
- Make message list header stay on top when scrolling (#353)
- Add support for 'enchant' spellcheck engine
- Check filetype detection in installer and update script (#4252)
- Fix folder names truncation in Classic skin (#4265)
- Make possible to disable some (broken) IMAP extensions with imap_disable_caps option (#4245)
- Contacts drag-n-drop default action is to move contacts (#3962)
- Added possibility to choose to move or copy contacts from drag-n-drop menu (#3962)
- Fix Close link and remove About link on error pages (#4201)
- Improved/unified attachment preview screen, added print button
- Fix lack of space between searchfiler and quicksearchbar in Larry skin (#4233)
- Cache LDAP's user_specific search and use vlv for better performance (#4247)
- LDAP: auto-detect and use VLV indices for all search operations
- LDAP: additional group configuration options for address books
- LDAP: separated address book implementation from a generic LDAP wrapper class
- Allow address books to browse a multi-level group hierarchy in the contacts list
- Fix session issues when local and database time differs (#2401)
- Fix thread cache syncronization/validation (#4150)
- Added feature to import messages to the currently selected folder
- Add option show_real_foldernames to disable localization of special folders
- Fix database cache expunge issues (#4229)
- Fix date format issues on MS SQL Server (#4078)
- Add imap_cache_ttl option to configure TTL of imap_cache
- Make LDAP cache engine configurable via ldap_cache and ldap_cache_ttl options
- Fix "duplicate entry" errors on inserts to imap cache tables (#4228)
- Improved handling of Reply-To/Bcc addresses of identity in compose form (#4142)
- Added user preference to open all popups as standard windows
- Implemented shared cache (rcube_cache_shared)
- Change Reply-All button label/title when mailing list is detected (#4092)
- Fix SMTP connection using IPv6 address in smtp_server option (#4147)
- Added attachment_reminder plugin
- Make PHP code eval() free, use create_function()
- Add option to display email address together with a name in mail preview (#3952)
- Support CSV import from Atmail (#4161)
- Add db_prefix configuration option in place of db_table_*/db_sequence_* options
- Make possible to use db_prefix for schema initialization in Installer (#4175)
- Fix updatedb.sh script so it recognizes also table prefix for external DDL files
- Fix parsing invalid date string (#4155)
- Add "with attachment" option to messages list filter (#1795)
- Call resize handler in intervals to prevent lags and double onresize calls in Chrome (#4137)
- Add rel="noreferrer" for links in displayed messages (#4976)
- Add ability to toggle between HTML and text while viewing a message (#3005)
- Remove "HTML message" from attachments list while viewing a message in text mode (#3005)
- Support IMAP MOVE extension [RFC 6851]
- Add attachment menu with Open and Download options (#4116)
- Display user-friendly message on IMAP "over quota" errors (#914)
- Extended archive plugin with user-configurable options to store messages into subfolders
- Fix export of selected contacts from search result (#4070)
- Feature to export only selected contacts from addressbook (by Phil Weir)
RELEASE 0.9.5
-------------
- Fix failing vCard import when email address field contains spaces (#4363)
- Fix default spell-check configuration after Google suspended their spell service
- Fix vulnerability in handling _session argument of utils/save-prefs [CVE-2013-6172] (#4362)
- Fix iframe onload for upload errors handling (#4361)
- Fix address matching in Return-Path header on identity selection (#4358)
- Fix text wrapping issue with long unwrappable lines (#4356)
- Fixed issues where HTML comments inside style tag would hang Internet Explorer
- Hide Delivery Status Notification option when smtp_server is unset (#4339)
- Display full attachment name using title attribute when name is too long to display (#4328)
- Fix attachment icon issue when rare font/language is used (#4334)
- Fix expanded thread root message styling after refreshing messages list (#4335)
- Fix issue where From address was removed from Cc and Bcc fields when editing a draft (#4327)
- Fix error_reporting directive check (#4331)
- Fix de_DE localization of "About" label in Help plugin (#4333)
RELEASE 0.9.4
-------------
- Make identities matching case insensitive (#1881)
- Fix issue where too big message data was stored in cache causing sql errors (#4325)
- Fix iframe scrollbars on webkit desktop browsers (#4319)
- Fix issue where legacy config was overridden by default config (#4305)
- Fix newmail_notifier issue where favicon wasn't changed back to default (#4324)
- Fix setting of Junk and NonJunk flags by markasjunk plugin (#4303)
- Fix lack of Reply-To address in header of forwarded message body (#4314)
- Fix bugs when invoking contact creation form when read-only addressbook is selected (#4313)
- Fix identity selection on reply (#4308)
- Fix so additional headers are added to all messages sent (#4302)
- Fix display issue after moving folder in Folder Manager (#4310)
- Fix handling of non-default date formats (#4311)
- Fix unquoted path in PREG expression on Windows (#4307)
- Fix wrong close tag in /template/mail.html (#4312)
RELEASE 0.9.3
-------------
- Fix setting refresh_interval to "Never" in Preferences (#4304)
- Fixed iframe scrolling on touch devices
- Optimized message list for touch devices
- Fix purge action in folder manager (#4300)
- Fix base URL resolving on attribute values with no quotes (#4297)
- Fix wrong handling of links with '|' character (#4298)
- Fix colorspace issue on image conversion using ImageMagick (#4294)
- Fix XSS vulnerability when editing a message "as new" or draft [CVE-2013-5645] (#4283)
- Fix XSS vulnerability when saving HTML signatures [CVE-2013-5645] (#4283)
- Fix rewrite rule in .htaccess (#4278)
- Fix detecting Turkish language in ISO-8859-9 encoding (#4284)
- Fix identity-selection using Return-Path headers (#4279)
- Fix parsing of links with ... in URL (#4251)
- Fix compose priority selector when opening in new window (#4286)
- Fix bug where signature wasn't changed on identity selection when editing a draft (#4272)
- Fix IMAP SETMETADATA parameters quoting (#4274)
- Fix "could not load message" error on valid empty message body (#4271)
- Fix handling of message/rfc822 attachments on message forward and edit (#4262)
- Fix parsing of square bracket characters in IMAP response strings (#4267)
- Don't clear References and in-Reply-To when a message is "edited as new" (#4263)
- Fix messages list sorting with THREAD=REFS
- Remove deprecated (in PHP 5.5) PREG /e modifier usage (#4239)
- Fix empty messages list when register_globals is enabled (#4232)
- Fix so valid and set date.timezone is not required by installer checks (#4242)
- Canonize boolean ini_get() results (#4249)
- Fix so install do not fail when one of DB driver checks fails but other drivers exist (#4240)
- Fix so exported vCard specifies encoding in v3-compatible format (#4244)
RELEASE 0.9.2
-------------
- Fix image thumbnails display in print mode (#4220)
- Fix height of message headers block (#4200)
- Fix timeout issue on drag&drop uploads (#4238)
- Fix default sorting of threaded list when THREAD=REFS isn't supported
- Fix list mode switch to 'List' after saving list settings in Larry skin (#4236)
- Fix error when there's no writeable addressbook source (#4235)
- Fix zipdownload plugin issue with filenames charset (#4231)
- Fix so non-inline images aren't skipped on forward (#4230)
- Fix "null" instead of empty string on messages list in IE10 (#4227)
- Fix legacy options handling
- Fix so bounces addresses in Sender headers are skipped on Reply-All (#4140)
- Fix bug where serialized strings were truncated in PDO::quote() (#4226)
- Fix displaying messages with invalid self-closing HTML tags (#4223)
- Fix PHP warning when responding to a message with many Return-Path headers (#4222)
- Fix unintentional compose window resize (#4206)
- Fix performance regression in text wrapping function (#4219)
- Fix connection to posgtres db using unix socket (#4218)
- Fix handling of comma when adding contact from contacts widget (#4199)
- Fix bug where a message was opened in both preview pane and new window on double-click (#4212)
- Fix fatal error when xdebug.max_nesting_level was exceeded in rcube_washtml (#4202)
- Fix PHP warning in html_table::set_row_attribs() in PHP 5.4 (#4194)
- Fix invalid option selected in default_font selector when font is unset (#4204)
- Fix displaying contact with ID divisible by 100 in sql addressbook (#4211)
- Fix browser warnings on PDF plugin detection (#4209)
- Fix fatal error when parsing UUencoded messages (#4210)
RELEASE 0.9.1
-------------
- Better German labels for from/to to avoid conflicts with 'sender' (#4188)
- Fix problem where security warning was displayed for valid images with image/jpg type (#4196)
- Fix handling of invalid email addresses in headers (#4193)
- Fix IMAP connection issue with default_socket_timeout < 0 and imap_timeout < 0 (#4191)
- Fix various PHP code bugs found using static analysis (#4190)
- Fix backslash character handling on vCard import (#4189)
- Fix csv import from Thunderbird with French localization (#4170)
- Fix messages list focus issue in Opera and Webkit (#4169)
- Fix Reply-To header handling in Reply-All action (#4157)
- Fix so Sender: address is added to Cc: field on reply to all (#4140)
- Fix so addressbook_search_mode works also for group search (#4183)
- Fix removal of a contact from a group in LDAP addressbook (#4185)
- Include SQL query in the log on SQL error (#4172)
- Fix handling untagged responses in IMAP FETCH - "could not load message" error (#4180)
- Fix very small window size in Chrome (#4087)
- Fix list page reset when viewing a message in Larry skin (#4182)
- Fix min_refresh_interval handling on preferences save (#4179)
- Fix PDF support detection for Firefox PDF.js (#4113)
- Fix possible collision in generated thumbnail cache key (#4177)
- Fix exit code on bootsrap errors in CLI mode (#4160)
- Fix error handling in CLI mode, use STDERR and non-empty exit code (#5161)
- Fix error when using check_referer=true
- Fix incorrect handling of some specific links (#4171)
- Fix incorrect handling of leading spaces in text wrapping
- Fix unintentional messages list jumps on click in Internet Explorer (#4167)
- Fix list of required configuration options (#4166)
- Fix DB error when creating a new contact and a group is selected (#4164)
- Fix handling of deprecated boolean value of reply_mode option (#4165)
RELEASE 0.9.0
-------------
- Fix display of HTML entities in protected folder name (#4159)
- Set minimal permissions to temp files (#4131)
- Improve content check for embedded images without filename (#4151)
- Fix handling of invalid characters in message headers and output (#4153)
- Fix selecting collapsed rows on select-all (#4156)
- Avoid race-conditions with concurrent attachment uploads (#3739)
- Fix possible header duplicates when using additional headers (#4154)
- Fix session issues with use_https=true (#4125)
- Fix blockquote width in sent mail (#4152)
- Fix keyboard events on list widgets in Internet Explorer (#4148)
RELEASE 0.9-rc2
---------------
- Fix security issue in save-pref command
- Remove sig_above configuration option, use reply_mode only (#4135)
- Refresh current folder in opener window after draft save or message sent (#4132)
- Fix saving draft just after entering compose window (#4141)
- Fix javascript error in IE9 when loading form with placeholders into an iframe (#4138)
- Fix handling of some conditional comment tags in HTML message (#4136)
- Fix so forward as attachment works if additional attachment is added by message_compose hook (#4134)
- Better handling of session errors in ajax requests (#4105)
- Fix HTML part detection for some specific message structures (#4130)
- Don't show fake address - phishing prevention (#4120)
- Fix forward as attachment bug with editormode != 1 (#4129)
- Fix LIMIT/OFFSET queries handling on MS SQL Server (#4123)
- Fix so task name can really contain all from a-z0-9_- characters (#4095)
- Fix javascript errors when working in a page opened with taget="_blank"
- Mention SQLite database format change in UPGRADING file (#4122)
- Increase maxlength to 254 chars for email input fields in addressbook (#4126)
- Fix thumbnail size when GD extension is used for image resize (#4124)
- Display notice that message is encrypted also for application/pkcs7-mime messages (#3815)
RELEASE 0.9-rc
--------------
- Fix plain text spellchecker incorrect highlighting in non-ASCII text (#4114)
- Add workaround for invalid message charset detection by IMAP servers (#4112)
- Fix NUL characters in content-type of ms-tnef attachment (#4108)
- Fix regression in handling LDAP contact identifiers (#4104)
- Updated translations from Transifex
- Fix buggy error template in a frame (#4092)
- Add addressbook widget on compose page in classic skin
- Add search box to compose address book widget (#3710)
- Fix login in case when default_host is an array with one element (#4085)
- Use LDAP fallback hosts on connect + bind instead of ldap_connect() only.
- Add config option for LDAP bind timeout (sets LDAP_OPT_NETWORK_TIMEOUT option)
- Submit Addressbook advanced search form with Enter key (#3843)
- Also block remote images in HTML part view (#4013)
- Improved database schema upgrade procedure, added updatedb.sh script
- Force autocommit mode in mysql database driver (#4068)
RELEASE 0.9-beta
----------------
- Fix searching by date in address book (#4058)
- Improve charset detection by prioritizing charset according to user language (#2032)
- Fix handling of escaped separator in vCard file (#4064)
- Add option to use envelope From address for MDN responses (#4052)
- Add possibility to search in message body only (#3977)
- Support "multipart/relative" as an alias for "multipart/related" type (#4057)
- Display PGP/MIME signature attachments as "Digital Signature" (#3845)
- Workaround UW-IMAP bug where hierarchy separator is added to the shared folder name (#4051)
- Fix version comparisons with -stable suffix (#4050)
- Add unsupported alternative parts to attachments list (#4046)
- Add Compose button on message view page (#3959)
- Display 'Sender' header in message preview
- Plugin API: Added message_before_send hook
- Fix contact copy/add-to-group operations on search result (#4042)
- Use matching identity in MDN response (#4043)
- Fix handling of signatures on draft edit (#3996)
- Fix so compacting of non-empty folder is possible also when messages list is empty (#4039)
- Allow forwarding of multiple emails (#2941)
- Fix big memory consumption of DB layer (#4037)
- Fix broken message/part bodies when FETCH response contains more untagged lines (#4020)
- Fix empty email on identities list after identity update (#4018)
- Add new identities_level: (4) one identity with possibility to edit only signature
- Use Delivered-To and Envelope-To headers for identity selection (#4024, #3835)
- Fix XSS vulnerability using Flash files (#4014)
- Always save drafts with format=flowed in order to keep original line wraps (#3997)
- Select default_addressbook on the list in Address Book (#3624)
- Fix so mobile phone has TYPE=CELL in exported vCard (#4004)
- Support contacts import from CSV file (#2605)
- Improved keep-alive action. Now the interval is based on session_lifetime (#3799)
- Added cross-task 'refresh' request for system state updates (#3799)
- Renamed config options: keep_alive to refresh_interval, min_keep_alive to min_refresh_interval
- Fix handling of text/enriched content on message reply/forward/edit
- Option to display attached images as thumbnails below message body
- Upgraded to jQuery 1.8.3 and jQuery UI 1.9.1
- Add config option to automatically generate LDAP attributes for new entries
- Add user settings to open message view and compose form in new windows (#1886)
- Better client-side timezone detection using the jsTimezoneDetect library (#3947)
- Add option to disable saving sent mail in Sent folder - no_save_sent_messages (#3923)
- Fix handling dont_override with message_sort_col and message_sort_order settings (#3970)
- Fix handling of URLs with asterisk characters (#3969)
- Remove automatic to-lowercase conversion of usernames (#3941)
- Plugin API: Add 'email_list' argument for identities data in user_create hook
- Integrated zipdownload plugin to download all attachments (#617)
- Fix HTML special characters handling in message list/header display (#3812)
- List related text/html part as attachment in plain text mode (#3918)
- Use IMAP BINARY (RFC3516) extension to fetch message/part bodies
- Fix folder creation under public namespace root (#3910)
- Fix so "Edit as new" on draft creates a new message (#3924)
- Fix invalid error message on deleting mail from read only folder (#3929)
- Replace data URIs of images (pasted in HTML editor) with inline attachments (#3795)
- Remove (too big) min-width on mail screen
- Added template object 'frame'
- Add option to enable HTML editor on forwarding (#3807)
- Add option to not include original message on reply, rename option top_posting to reply_mode (#1615)
- Added session_path config option and unified cookies settings in javascript
- Added "Undeleted" option to messages list filter
- Rewritten test scripts for PHPUnit
- Add new DB abstraction layer based on PHP PDO, supporting SQLite3 (#3668)
- Removed PEAR::MDB2 package
- Removed users.alias column, added option ('user_aliases')
to use email address from identities as username (#3851)
- Removed redundant cache.cache_id column (#3817)
- Fix order of attachments in sent mail (#3740)
- Fix Shift + delete button does not permanently delete messages (#3598)
- Add Content-Length for attachments where possible (#1880)
- Fix attachment sizes in message print page and attachment preview page (#3805)
- Add mail attachments using drag & drop on HTML5 enabled browsers
- Add workaround for invalid BODYSTRUCTURE response - parse message with Mail_mimeDecode package (#1966)
- Display Tiff as Jpeg in browsers without Tiff support (#3757)
- Don't display Pdf/Tiff/Flash attachments inline without browser support (#3757, #3394)
- Add is_escaped attribute for html_select and html_textarea (#3782)
- Fix issue where draft auto-save wasn't executed after some inactivity time
- Add vCard import from multiple files at once (#3458)
- 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)
Better classes separation
Framework files moved to lib/Roundcube
RELEASE 0.8.5
-------------
- Fix #countcontrols issue in IE<=8 when text is very long (#4060)
- Fix unwanted horizontal scrollbar in message preview header (#4044)
- Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#4028)
- Fix XSS vulnerability in vbscript: and data:text links handling [CVE-2012-6121] (#4033)
- Fix absolute positioning in HTML messages (#4007)
- Fix cache (in)validation after setting \Deleted flag
- Fix keybord events on messages list in opera browser (#4011)
- Fix selection of collapsed thread rows (#3978)
- Fix wrapping of quoted text with format=flowed (#3561)
RELEASE 0.8.4
-------------
- Fix regression where unintentional page reload was done after request abort (#3999)
- Fix XSS vulnerability in handling of text/enriched messages (#4000)
- Fix handling of 'media' attribute on linked css (#3989)
- Fix excessive LFs at the end of composed message with top_posting=true (#3995)
- Fix bug where leading blanks were stripped from quoted lines (#3994)
RELEASE 0.8.3
-------------
- Fix AREA links handling (#3992)
- Fix possible HTTP DoS on error in keep-alive requests (#3983)
- Fix compatybility with MDB2 2.5.0b4 (#3982)
- Fix a bug where saving a message in INBOX wasn't possible
- Fix HTML part detection in messages with attachments (#3976)
- Fix bug where wrong words were highlighted on spell-before-send check
- Fix scrolling quirk in email preview frame using Opera 12 (#3973)
- Fix displaying of multipart/alternative messages with empty parts (#3961)
- Fix threaded list sorting on PHP < 5.2.9 (#3960)
- Fix Warning: htmlspecialchars(): charset `RCMAIL_CHARSET' not supported warning in Installer (#3958)
RELEASE 0.8.2
-------------
- Fix XSS vulnerability from HTTP User-Agent header (#3954)
- Force fonts in compose fields to be all the same (#3926)
- Fix handling vCard entries with TEL;TYPE=CELL (#3949)
- Fix error where session wasn't updated after folder rename/delete (#3928)
- Fix PLAIN authentication for some IMAP servers (#3916)
- Fix encoding vCard file when contains PHOTO;ENCODING=b (#3922)
- Fix focus issue in IE when selecting message row (#3881)
- Add full headers view in message preview window (#3823)
- Fix message display page issues - unified with message preview (#3856, #3895)
- Fix displaying all headers when they contain malformed characters (#3911)
- Fix decoding of HTML messages with UTF-16 charset specified (#3902)
- Fix quota capability detection so it can be overwritten by a plugin (#3903)
- Fix identity selection on reply (#3516)
- Fix Larry's messages list filter in IE (#3890)
- Fix more IE issues by disabling Compat. mode with X-UA-Compatible meta tag (#3886)
- Fix setting locales under Solaris - use additional .UTF-8 suffix (#3887)
- Fix email address validation for addresses with IP address in domain part
- Fix Larry skin issues in IE7 compat. mode (#3879)
- Fix so subscribed non-existing/non-accessible shared folder can be unsubscribed
RELEASE 0.8.1
-------------
- Fix bug where domain name was converted to lower-case even with login_lc=false (#3859)
- Fix lower-casing email address on replies (#3863)
- Fix line separator in exported messages (#3866)
- Fix XSS issue where plain signatures wasn't secured in HTML mode [CVE-2012-4668] (#3875)
- Fix XSS issue where href="javascript:" wasn't secured [CVE-2012-3508] (#3875)
- Fix impossible to create message with empty plain text part (#3873)
- Fix stripped apostrophes when replying in plain text to HTML message (#3869)
- Fix inactive Save search option after advanced search (#3870)
- Fix Remove from group option is active for contact search result (#3871)
- Disable autocapitalization in login form on iPad/iPhone (#3872)
- Fix focus on the list when list row is clicked (#3865)
- Added separate From and To columns apart from smart From/To column (#2970)
- Fix fallback to Larry skin when configured skin isn't available (#3857)
- Fix (workaround) delete operations with some versions of memcache (#3858)
- 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 (#3848)
- Enable TinyMCE inlinepopups plugin
- Update to TinyMCE 3.5.6
- Correctly escape localized labels in javascript variable (#3842)
- Update Net_SMTP/Auth_SASL packages to fix Digest-MD5/Cram-MD5 authentication (#3846)
- Don't add attachments content into reply/forward/draft message body (#3837)
- Fix 'no connection' errors on page unloads (#3832)
- Plugin API: Add 'unauthenticated' hook (#3545)
- Show explicit error message when provided hostname is invalid (#3834)
- Fix wrong compose screen elements focus in IE9 (#3826)
- Fix fatal error when date.timezone isn't set (#3831)
- Update to TinyMCE 3.5.4.1
- Better icons with distinct shapes for priority columns (#3706)
- Show dedicated icon for multipart/report messages (#3813)
- Properly hide text of icon links/buttons (#3820)
- Fix handling of unitless CSS size values in HTML message (#3821)
- Fix removing contact photo using LDAP addressbook (#3737)
- Fix storing X-ANNIVERSARY date in vCard format (#3816)
- Update to Mail_Mime-1.8.5 (#3810)
- Fix XSS vulnerability in message subject handling using Larry skin [CVE-2012-3507] (#3809)
- Fix handling of links with various URI schemes e.g. "skype:" (#3521)
- 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 (#3803)
- Fix errors with enabled PHP magic_quotes_sybase option (#3798)
- Fix SQL query for contacts listing on MS SQL Server (#3797)
- Fix window.resize handler on IE8 and Opera (#3758)
- Don't let error message popups cover the login form (#3794)
- Update to TinyMCE 3.5.2
- Don't show errors when moving contacts into groups they are already in (#3788)
- Make folders with unread messages in subfolders bold again (#2892)
- Abbreviate long attachment file names with ellipsis (#3793)
- Fix html2text conversion of strong|b|a|th|h tags when used in upper case
- Add listcontrols template container in Larry skin (#3792)
- Fix host autoselection when default_host is an array (#3790)
- Move messages forwarding mode setting into Preferences
- Fix HTML entities handling in HTML editor (#3780)
- Fix listing shared folders on Courier IMAP (#3767)
RELEASE 0.8-rc
--------------
- Added new translations in Belarusian, Interlingua and Malayalam
- Flipped compose options arrow (#3772)
- Fix handling of large uuencode attachments (#3771)
- Fix handling of "usemap" attribute (#3770)
- Fix handling of some HTML tags e.g. IMG (#3769)
- Use similar language as a fallback for plugin localization (#3726)
- Fix issue where signature wasn't re-added on draft compose (#3659)
- Update to TinyMCE 3.5 (#3762)
- 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 (#3755)
- Fix redirect to mail/compose on re-login (#3585)
- Add IE8 hack for messages list issue (#3317)
- Fix handling errors on draft auto-save
- Fix importing vCard photo with ENCODING param specified (#3746)
- Support multiple name/email pairs for Bcc and Reply-To identity settings (#3752)
- Set flexible width to login form fields (#3735)
- Fix re-draw bug on list columns change in IE8 (#3318)
- Allow mass-removal of addresses from a group (#3259)
- Fix removing all contacts on import to LDAP addressbook
- Fix so "Back" from compose/show doesn't reset search request (#3594)
- Add option to delete messages instead of moving to Trash when in Junk folder (#2805)
- Fix invisible cursor when replying to a html message (#3100)
- Reset IP stored in session when destroying session data (#3485)
- Fix bug where memory_limit = -1 wasn't handled properly
- Support LDAP RFC2256's country object class read/write (#3535)
- Upgraded to jQuery 1.7.2
- Image resize with GD extension (#3712)
- Fix lack of warning when switching task in compose window (#3725)
- Fix bug where it wasn't possible to enter ( or & characters in autocomplete fields
- Request all needed fields from address book backends (#3721)
- Unified (single) spellchecker button
- Scroll long lists on drag&drop (#2249)
- Copy all skins in installto script (#3705)
RELEASE 0.8-beta
----------------
- Upgraded to jQuery 1.7.1 (#3673) and jQuery UI 1.8.18
- Add Russian to the spellchecker languages list (#3542)
- Remember custom skin selection after logout (#3688)
- Make sure About tab is always the last tab (#3609)
- Fix issue with folder creation under INBOX. namespace (#3683)
- Added mailto: protocol handler registration link in User Preferences (#2729)
- Handle identity details box with an iframe (#3066)
- Fix issue where some text from original message was missing on reply (#3675)
- Fix autoselect_host() for login (#3639)
- 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 (#1973)
- Add separate pagesize setting for mail messages and contacts (#3617)
- Deprecate $DB, $USER, $IMAP global variables, Use $RCMAIL instead
- Add option to set default font for HTML message (#894)
- Fix issues with big memory allocation of IMAP results
- Prevent from memory_limit exceeding when trying to parse big messages bodies (#3164)
- Add possibility to add SASL mechanisms for SMTP in smtp_connect hook (#3399)
- Mark (with different color) folders with recent messages (#2479)
- Added About tab in Settings
- TinyMCE updated to 3.4.6
RELEASE 0.7.2
-------------
- Fix encoding of attachment with comma in name (#3717)
- Fix handling of % character in IMAP protocol (#3711)
- Fix duplicate names handling in addressbook searches (#3704)
- Fix displaying of HTML messages from Disqus (#3702)
- Disable E_STRICT warnings on PHP 5.4
- Prevent from folder selection on virtual folder collapsing (#3681)
- 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 (#3664)
- Add lost translation label in de_DE (#3654)
- Fix drafts update issues when edited from preview pane (#3653)
- Fix wrong variable name in rcube_ldap.php (#3643)
- 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 (#3642)
RELEASE 0.7.1
-------------
- Fix bug in handling of base href and inline content (#3634)
- Fix SQL Error when saving a contact with many email addresses (#3630)
- Fix strict email address searching if contact has more than one address
- Remove duplicated 'organization' label (#3631)
- Fix so editor selector is hidden when 'htmleditor' is listed in 'dont_override'
- Fix wrong (long) label usage (#3627)
- Fix handling of INBOX's subfolders in special folders config (#3623)
- Add ifModule statement for setting Options -Indexes in .htaccess file (#3620)
- Fix crashes with eAccelerator (#3608)
- Fix searching on IMAP servers without CHARSET specifier support (#3619)
- Fix expanding folders during drag&drop (#3611)
- Fix wrong postgres sequence name in upgrade from 0.6
- Fix broken CREATE INDEX queries in SQLite DDL files (#3607)
RELEASE 0.7
-----------
- Make Roundcube render the Email Standards Project Acid Test correctly
- Replace prompt() with jQuery UI dialog (#1603)
- Fix navigation in messages search results
- Improved handling of some malformed values encoded with quoted-printable (#3590)
- Add possibility to do LDAP bind before searching for bind DN
- Fix handling of empty <U> tags in HTML messages (#3584)
- Add content filter for embedded attachments to protect from XSS on IE [CVE-2012-1253] (#3372)
- Use strpos() instead of strstr() when possible (#3581)
- Fix handling HTML entities when converting HTML to text (#3582)
- Fix fit_string_to_size() renders browser and ui unresponsive (#3577)
- Fix handling of invalid characters in request (#3536)
- Fix merging some configuration options in update.sh script (#2181)
- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#3578)
- Fix handling contact photo url with https:// prefix (#3575)
- Fix possible infinite redirect on attachment preview (#3572)
- Improved clickjacking protection for browsers which don't support X-Frame-Options headers
- Fixed bug where similar folder names were highlighted wrong (#3345)
- Fixed bug in handling link with '!' character in it (#3569)
- Fixed bug where session ID's length was limited to 40 characters (#3570)
- 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 (#1604)
- Fix regression in setting recipient to self when replying to a Sent message (#3101)
- Fix listing of folders in hidden namespaces (#2895)
- Don't consider \Noselect flag when building folders tree (#3448)
- Fix sorting autocomplete results (#3504)
- Add option to set session name (#2630)
- 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 (#3312)
- Add option to define matching method for addressbook search (#2720, #3378)
- Make email recipients separator configurable
- Fix so folders with \Noinferiors attribute aren't listed in parent selector
- Fix handling of curly brackets in URLs (#3555)
- Fix handling of dates (birthday/anniversary) in contact data (#3552)
- Fix error on opening searched LDAP contact (#3550)
- Fix redundant line break in flowed format (#3551)
- Fix IDN address validation issue (#3544)
- Fix JS error when dst_active checkbox doesn't exist (#3540)
- Autocomplete LDAP records when adding contacts from mail (#3498)
- Plugin API: added 'ready' hook (#3492)
- Ignore DSN request when it isn't supported by SMTP server (#3300)
- Make sure LDAP name fields aren't arrays (#3523)
- Fixed imap test to non-default port when using ssl (#3532)
- Force all files to be overwritten when updating (#3531)
- Fix issue where it wasn't possible to change list view mode in folder manager for INBOX (#3522)
- Fix namespace handling in special folders settings (#3527)
- Disable time limit for CLI scripts (#3524)
- Fix misleading display when chaning editor type (#3519)
- Add loading indicator on contact delete
- Fix bug where after delete message rows can be added to the list of another folder (#3263)
- 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 (#3503)
- Make date/time format user configurable; drop 'date_today' config option
- Fix setting title for truncated subject in IE (#3141)
- Fix displaying multipart/alternative messages with only one part (#3400)
- 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 (#3462)
- 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 (#2884)
- Localize forwarded message header (#3487)
RELEASE 0.6
-----------
- Fix bug where the last identity is used on reply (#3516)
- Fix locked folder rename option on servers supporting RFC2086 only (#3508)
- Fix session race conditions when composing new messages
- Fix encoding of LDAP contacts identifiers (#3501)
- jQuery 1.6.4
- Fix handling of binary attachments encoded with quoted-printable (#3494)
- Fix text-overflow:ellipsis issues on messages list in FF7 and Webkit (#3490)
- Fix handling of links with IP address
- Fix compacting folder resets message list filter (#3499)
RELEASE 0.6-rc
----------------
- Send X-Frame-Options headers to protect from clickjacking (#3079)
- Fallback to mail_domain in LDAP variable replacements; added 'host' to 'user_create' hook arguments (#3464)
- Fixed wrong vCard type parameter mobile (#3496)
- Fixed vCard WORKFAX issue (#3476)
- Add vCard's Profile URL support (#3491)
- jQuery 1.6.3
- Fix imap_cache setting to values other than 'db' (#3489)
- Fix handling of attachments inside message/rfc822 parts (#3466)
- Make list of mimetypes that open in preview window configurable (#3175)
- 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 (#3434)
- Add option to hide selected LDAP addressbook on the list
- Add client-side checking of uploaded files size
- Add newlines between organization, department, jobtitle (#3468)
- Recalculate date when replying to a message and localize the cite header (#3212)
- Fix handling of email addresses with quoted local part (#3401)
- Fix EOL character in vCard exports (#3357)
- Added optional "multithreading" autocomplete feature
- Plugin API: Added 'config_get' hook
- Fixed new_user_identity plugin to work with updated rcube_ldap class (#3443)
- 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 (#3258)
- Add optional textual upload progress indicator (#2330)
- Fix parsing URLs containing commas (#3425)
- Added vertical splitter for books/groups list in addressbook (#3389)
- 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 (#3406)
- Added addressbook advanced search
- Add popup with basic fields selection for addressbook search
- Case-insensitive matching in autocompletion (#3398)
- Added option to force spellchecking before sending a message (#1862)
- Fix handling of "<" character in contact data, search fields and folder names (#3349)
- Fix saving "<" character in identity name and organization fields (#3349)
- 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 (#3390)
- 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 (#3380)
- Add forward-as-attachment feature
- jQuery-1.6.2 (#5158, #3154)
- Improve display name composition when saving contacts (#3153)
- Fix problems with subfolders of INBOX folder on some IMAP servers (#3247)
- Fix handling of folders that doesn't belong to any namespace (#3184)
- Enable multiselection for attachments uploading in capable browsers (#2266)
- Add possibility to change HTML editor configuration by skin
- Fix a bug where selecting too many contacts would produce too large URI request (#3369)
- Improve performance by including files with absolute path (#3337)
- Move folder name truncation to client/skin (#1822)
- Added plugin hook for request token creation
- Replace LDAP vars in group queries (#3329)
- Fix vcard folding with uncode characters (#3353)
- Keep all submitted data if contact form validation fails (#3350)
- Handle uncode strings in rcube_addressbook::normalize_string() (#3351)
- Fix handling of debug_level=4 in ajax requests (#3327)
- Enable TinyMCE's contextmenu (#3062)
- 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 (#3306)
- Better display of vcard import results (#1861)
- 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 (#2810)
- Replying to a sent message puts the old recipient as the new recipient (#3101)
- 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 [CVE-2011-2937] (#3469)
RELEASE 0.5.3
-------------
- Fix identities "reply-to" and "bcc" fields have a bogus value when left empty (#3405)
- Fix issue which cases IMAP disconnection when encrypt() method was used (#3374)
- 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 (#3376)
- Fix relative URLs handling according to a <base> in HTML (#3368)
- Fix handling of top-level domains with more than 5 chars or unicode chars (#3366)
- Fix usage of non-standard HTTP error codes (#3297)
- Fix PHP warning on mistaken in_array() usage (#3375)
RELEASE 0.5.2
-------------
- TinyMCE 3.4.2 now compatible with IE9
- PEAR::Net_SMTP 1.5.2, fixed timeout issue (#3332)
- 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 (#3348)
- Improve space-stuffing handling in format=flowed messages (#3346)
- Fix bug where some dates would produce SQL error in MySQL (#3342)
- Added workaround for some IMAP server with broken STATUS response (#3344)
- Fix bug where default_charset was not used for text messages (#3328)
- Stateless request tokens. No keep-alive necessary on login page (#3325)
- 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 (#2516)
- Fix some emails are not shown using Cyrus IMAP (#3316)
- Fix handling of mime-encoded words with non-integral number of octets in a word (#3301)
- Fix parsing links with non-printable characters inside (#3305)
- Fixed de_CH Localization bugs (#3279)
- Add variable for 'Today' label in date_today option (#2394)
- Fix dont_override setting does not override existing user preferences (#3205)
- 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 (#3281)
RELEASE 0.5.1
-------------
- Fix handling of attachments with invalid content type (#3275)
- Add workaround for DBMail's bug http://www.dbmail.org/mantis/view.php?id=881 (#3274)
- 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 (#3251)
- Fix ICANN example addresses doesn't validate (#3253)
- Security: protect login form submission from CSRF [CVE-2011-1491]
- Security: prevent from relaying malicious requests through modcss.inc [CVE-2011-1492]
- Fix handling of non-image attachments in multipart/related messages (#3261)
- Fix IDNA support when IDN/INTL modules are in use (#3253)
- Fix handling of invalid HTML comments in messages (#3269)
- Fix parsing FETCH response for very long headers (#3264)
- Fix add/remove columns in message list when message_sort_order isn't set (#3262)
- Check mime headers before attempt to parse them (#3256)
- Quote header values in show_additional_headers plugin (#3255)
- Fix settings UI on IE 6 (#3246)
- Remove double borders in folder listing (#3236)
- Separate full message headers UI element from headers table (#3238)
- Add part MIME ID to message_part_* hooks (#3241)
- Improve parsing of MS Outlook vCards (#3239)
- 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. (#3210)
- Show full mail subject as title when hovering a cut subject link (#3141)
- Fix randomly disappearing folders list in IE (#3231)
- Fix list column add/removal in IE (#3230)
- Fix login redirect issues (#3221)
- Require PHP 5.2.1 or greater
- Fix %h/%z variables in username_domain option (#3228)
- Workaround for setting charset in case of malformed bodystructure response (#3227)
- Fix impossible to subscribe to protected folders (#3199)
- Fix setting timezone in Preferences (#3232)
RELEASE 0.5
-----------
- Fix double-login/session issue (#3124)
- Wrap HTML parts with <html><body> and add Doctype declaration (#3119)
- Make rcube_autoload silently skip unknown classes (#3128)
- Fix charset detection in vcards with encoded values (#1934)
- Better CSS cursors for splitters (#2954)
- Show the same message only once (#3186)
- Fix namespaces handling (#3192)
- Add handling of multifolder METADATA/ANNOTATION responses
- Fix handling of INBOX when personal namespace prefix is non-empty (#3200)
- Fix handling square brackets in links (#3209)
- Add description of 'use_https' option in main.inc.php.dist file
RELEASE 0.5-RC
--------------
- Plugin API: Add 'pass' argument in 'authenticate' hook (#3147)
- Fix attachments of type message/rfc822 are not listed on attachments list
- Add 'login_lc' config option for case-insensitive authentication (#3131)
- Fix window is blur'ed in IE when selecting a message (#3161)
- Fix cursor position on compose form in Webkit browsers (#2796)
- Fix setting charset of attachment filenames (#3136)
- Allow setting autocomplete attribute for all inputs separately (#3158)
- New Folder Manager UI
- Fix invalid Request when creating a folder (#3165)
- Add folder size and quota indicator in folder manager (#2112)
- Add possibility to move a subfolder into root folder (#2890)
- 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 (#1657)
- Improve performance of folder rename and delete actions
- Better support for READ-ONLY and NOPERM responses handling (#3108)
- Add confirmation message on purge/expunge command response
- Fix handling of untagged responses for AUTHENTICATE command (#3171)
- Add username and IP address to log message on unsuccessful login (#3176)
- Improved Mail-Followup-To and Mail-Reply-To headers handling
- Fix charset conversion for text attachments without charset specification (#3181)
RELEASE 0.5-BETA
----------------
- Make session data storage more robust against garbage session data (#3148)
- Config option for autocomplete on login screen
- Allow plugin templates to include local files (#3146)
- List groups in address detail view and allow to subscribe/unsubscribe from there (#2862)
- Messages caching: performance improvements, fixed syncing, fixes related with #2857
- Add link to identities in compose window (#2843)
- Add Internationalized Domain Name (IDNA) support (#729)
- Add option to automatically send read notifications for known senders (#2199)
- Add option to "Return receipt" will be always checked (#2571)
- Fix HTML to plain text conversion doesn't handle citation blocks (#2992)
- Use custom sorting when SORT is disabled by IMAP admin (#3020)
- Allow setting some washtml options from plugin (#2727)
- Add option do bind for an individual LDAP address book (#3048)
- Change reply prefix to display email address only if sender name doesn't exist (#2709)
- Plugin API: improved 'abort' flag handling, added 'result' item in some hooks (#2988)
- Fix mailto optional params in plain text messages aren't handled (#3071)
- Add Reply-to-List feature (#977)
- Add Mail-Followup-To/Mail-Reply-To support (#1937)
- Fix confirmation message isn't displayed after sending mail on Chrome (#2437)
- Fix keyboard doesn't work with autocomplete list with Chrome (#3073)
- Improve tabs to fixed width and add tabs in identities info (#3030)
- 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 (#2164)
- Use empty envelope sender address for message disposition notifications (RFC 2298.3)
- Support SMTP Delivery Status Notifications - RFC 3461 (#2409)
- Use css sprite image for messages list
- Add (different) attachment icon for messages of type multipart/report (#2426)
- Prevent from inserting empty link when composing HTML message (#3007)
- Add caching support in id2uid and uid2id functions (#3065)
- Add SASL proxy authentication for SMTP (#2811)
- Improve displaying of UI messages (#3033)
- Fix double e-mail filed in identity form (#3088)
- Display IMAP errors for LIST/THREAD/SEARCH commands (#2981)
- Add LITERAL+ (IMAP4 non-synchronizing literals) support (RFC 2088)
- Add separate column for message status icon (#2788)
- 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 (#3097)
- Fix handling of attachments when Content-Disposition is not inline nor attachment (#3086)
- Improve performance of unseen messages counting (#3090)
- 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 (#2808)
- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)
- Fix parent folder with unread subfolder not bold when message is open (#3104)
- 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 (#2474)
- Fix handling of URLs with tilde (~) or semicolon (;) character (#3110, #3111)
- 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 (#3116)
- Add option to place replies in the folder of the message being replied to (#2248)
- Add missing confirmation/error messages on contact/group/message actions (#2935)
- Add 'loading' message on message move/copy/delete/mark actions
- Improve responsiveness of messages displaying (#3039)
- Add option for minimum length of autocomplete's string (#2625)
- Fix operations on messages in unsubscribed folder (#3126)
- Add support for shared folders (#525)
- Fix handling of folders with name "0" (#3133)
- 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 (#3137)
- Fix error in MSSQL DDL scripts (#3130)
- Lock submit button in onsubmit event on login page (#3078)
- Don't set attachment's charset in Content-type header (#3136)
- Fix handling of message bodies (quoted-printable encoded) with NULL characters (#2448)
- 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 (#3067)
- 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 (#3069)
- Fix handling of Thunderbird's vCards (#3070)
RELEASE 0.4.1
-------------
- Fix space-stuffing in format=flowed messages (#3064)
- Fix msgexport.sh now using the new imap wrapper
- Avoid displaying password on shell (#3010)
- Only lower-case user name if first login attempt failed (#2600)
- Make alias setting in squirrelmail_usercopy plugin configurable (patch by pommi, #3056)
- Prevent from saving a non-existing skin path in user prefs (#3004)
- Improve handling of single-part messages with bogus BODYSTRUCTURE (#2976)
- Fix path to SQL files when using pgsql/mysqli/sqlsrv drivers (#2979)
- Fix upgrade script for SQLite (#2980)
- Fixes in SQL init script + added update script for MSSQL database
- Remove redundant date in syslog messages (#3008)
- Fix contacts list page controls when a group is selected (#3009)
- Fix SMTP test in Installer (#3014)
- Fix "Select all" causes message to be opened in folder with exactly one message (#2987)
- Fix Tab key doesn't work in HTML editor in Google Chrome (#2995)
- Fix TinyMCE uses zh_CN when zh_TW locale is set (#2998)
- Fix TinyMCE buttons are hidden in Opera (#2993)
- Fix JS error on IE when trying to send HTML message with enabled spellchecker (#3006)
- Display inline images with known extensions and non-image content-type (#3002)
- Fix "Threaded" checkbox after subfolder creation (#2997)
- Fix timezone string in sent mail (#3021)
- Show disabled checkboxes for protected folders instead of dots (#1898)
- Added fieldsets in Identity form, added 'identity_form' hook
- Re-added 'Close' button in upload form (#2999, #2917)
- Fix handling of charsets with LATIN-* label
- Fix messages background image handling in some cases (#3043)
- Fix format=flowed handling (#3042)
- Fix when IMAP connection fails in 'get' action session shouldn't be destroyed (#3046)
- Fix list_cols is not updated after column dragging (#3050)
- Support %z variable in host configuration options (#3054)
RELEASE 0.4
-----------
- Fix disappearing upload form disappears when user selects a file on Safari (#2917)
- Don't replace error messages with loading info (#2534)
- Fix JS errors on compose mode switch (#2952)
- Fix message structure parsing when it lacks optional fields (#2960)
- Include all recipients in sendmail log
- Support HTTP_X_FORWARDED_PROTO header for HTTPS detecting (#2950)
- Fix default IMAP port configuration (#2948)
- Create Sent folder when starting to compose a new message (#2900)
- Fix handling of messages with Content-Type: application/* and no filename (#840)
- Improved compose screen: resizable body and attachments list, vertical splitter, options menu
- Fix RC forgets search results (#722)
- TinyMCE 3.3.7
- Improve parsing of styled empty tags in HTML messages (#2908)
- Add %dc variable support in base_dn/bind_dn config (#2881)
- Add button to hide/unhide the preview pane (#955)
- Fix no-cache headers on https to prevent content caching by proxies (#2897)
- Fix attachment filenames broken with TNEF decoder using long filenames (#2894)
- Use user's timezone in Date header, not server's timezone (#2393)
- Add option to set separate footer for HTML messages (#2784)
- Add real SMTP error description to displayed error messages (#2233)
- Fix some IMAP errors handling when opening the message (#1848)
- Fix related parts aren't displayed when got mimetype other than image/* (#2629)
- Multiple identity and database support for squirrelmail_usercopy plugin (#2686)
- Support dynamic hostname (%d/%n) variables in configuration options (#1843)
- Add 'messages_list' hook (#2504)
- Add request* event triggers in http_post/http_request (#2340)
- Fix use RFC-compliant line-delimiter when saving messages on IMAP (#2828)
- Add 'imap_timeout' option (#2869)
- Fix forwarding of messages with winmail attachments
- Fix handling of uuencoded attachments in message body (#2163)
- Added list_mailboxes hook in rcube_imap::list_unsubscribed() (#2791)
- Fix wrong message on file upload error (#2839)
- Add support for data URI scheme [RFC2397] (#2851)
- 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 (#1052), fixes word wrapping issues (#2703)
- Fix duplicated attachments when forwarding a message (#2670)
- Fix message/rfc822 attachments containing only attachments are not parsed properly (#2854)
- Fix %00 character in winmail.dat attachments names (#2850)
- Fix handling errors of folder deletion (#2821)
- Parse untagged CAPABILITY response for LOGIN command (#2853)
- 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 (#2802)
- Fix check-recent action issues and performance (#2690)
- Fix messages order after checking for recent (#1249)
- Fix autocomplete shows entries without email (#2640)
- Fix listupdate event doesn't trigger on search response (#2824)
- Fix select_all_mode value after selecting a message (#2834)
- Set focus to editor on reply in HTML mode (#2768)
- Fix composing in HTML jumps cursor to body instead of recipients (#2796)
- Allow columns order change per user - drag&drop (#2124)
- Add References header in read receipt (#2801)
- Fix database constraint violation when opening a message (#2814)
- Add 'loading' message while login is in progress (#2790)
- Fix quota_zero_as_unlimited (#2786)
- Fix folder subscription checking (#2804)
- Fix INBOX appears (sometimes) twice in mailbox list (#2794)
- Fix listing of attachments of some types e.g. "x-epoc/x-sisx-app" (#2779)
- Fix DB Schema checking when some db_table_* options are not set (#2780)
RELEASE 0.4-beta
----------------
- Add sizelimit and timelimit variables in LDAP config (#2704)
- Hide IMAP host dropdown when single host is defined (#2553)
- Add images pre-loading on login page (#623)
- Add HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR to successful logins log (#2634)
- Fix setting spellcheck languages with extended codes (#2747)
- Fix messages list scrolling in FF3.6 (#2657)
- Fix quicksearch input focus (#2770)
- Always set changed date when flagging a DB record as deleted + provide a cleanup script
- Fix address book/group selection (#2760)
- Assign newly created contacts to the active group (#2764)
- Added option not to mark messages as read when viewed in preview pane (#1513)
- Allow plugins modify the Sent folder when composing (#2708)
- Added optional (max_recipients) support to restrict total number of recipients per message (#1167)
- Re-organize editor buttons, add blockquote and search buttons
- Make possible to write inside or after a quoted html message (#1878)
- Fix bugs on unexpected IMAP connection close (#2449, #2507)
- 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 (#2627)
- Added possibility to select all messages in a folder (#1312)
- Added 'imap_force_caps' option for after-login CAPABILITY checking (#2087)
- Password: Support dovecotpw encryption
- TinyMCE 3.3.1
- Implemented messages copying using drag&drop + SHIFT (#863)
- Improved performance of folders operations (#2689)
- Fix blocked.gif attachment is not attached to the message (#2685)
- Managesieve: import from Horde-INGO
- Managesieve: support for more than one match (#2362)
- Managesieve: support for selectively disabling rules within a single sieve script (#2198)
- 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 (#2413)
- Options virtuser_* replaced with virtuser_* plugins
- Plugin API: Implemented 'email2user' and 'user2email' hooks
- Fix forwarding message omits CC header (#2538)
- Add 'default_charset' option to user preferences (#1855)
- 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 (#2533)
- Fix deleting all messages from last list page (#2528)
- Flag original messages when sending a draft (#2458)
- Changed signature separator when top-posting (#2555)
- Let the admin define defaults for search modifiers (#2211)
- Fix long e-mail addresses validation (#2641)
- Remember search modifiers in user prefs (#2411)
- Added force_7bit option to force MIME encoding of plain/text messages (#2679)
- Use case sensitive check when checking for default folders (#2567)
- Fix checking for new mail: now checks unseen count of inbox (#2123)
- Improve performance by avoiding unnecessary updates to the session table (#2552)
- Fix invalid <font> tags which cause HTML message rendering problems (#2687)
- Fix CVE-2010-0464: Disable DNS prefetching (#2639)
- Fix Received headers to behave better with SpamAssassin (#2682)
- Password: Make passwords encoding consistent with core, add 'password_charset' global option (#2658)
- Fix adding contacts SQL error on mysql (#2645)
- Squirrelmail_usercopy: support reply-to field (#2678)
- Fix IE spellcheck suggestion popup issue (#2656)
- Fix email address auto-completion shows regexp pattern (#2498)
- Fix merging of configuration parameters: user prefs always survive (#2584)
- Fix quota indicator value after folder purge/expunge (#2671)
- Fix external mailto links support for use as protocol handler (#2328)
- Fix attachment excessive memory use, support messages of any size (#1245)
- Fix setting task name according to auth state
- Password: fix vpopmaild driver (#2662)
- Add workaround for MySQL bug [http://bugs.mysql.com/bug.php?id=46293] (#2659)
- Fix quoted text wrapping when replying to an HTML email in plain text (#897)
- Fix handling of extended mailto links (with params) (#2573)
- Fix sorting by date of messages without date header on servers without SORT (#2521)
- Fix inconsistency when not using default table names (#2652)
- Fix folder rename/delete buttons do not appear on creation of first folder (#2653)
- Fix character set conversion fails on systems where iconv doesn't accept //IGNORE (#2590)
- 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 (#2602)
- Use jQuery-1.4
- Removed problematic browser-caching of messages
- Fix incompatybility with suhosin.executor.disable_emodifier (#2549)
- Use PLAIN auth when CRAM fails and imap_auth_type='check' (#2587)
- Fix removal of <title> tag from HTML messages (#2629)
- Fix 'force_https' to specified port when URL contains a port number (#2612)
- Fix to-text converting of HTML entities inside b/strong/th/hX tags (#2621)
- Bug in spellchecker suggestions when server charset != UTF8 (#2607)
- Managesieve: Fix requires generation for multiple actions (#2603)
- Fix LDAP problem with special characters in RDN (#2548)
- Improved handling of message parts of type message/rfc822
- Plugin API: added 'quota' hook
- Fix parsing conditional comments in HTML messages (#2569)
- Use built-in json_encode() for proper JSON format in AJAX replies
- Allow setting only selected params in 'message_compose' hook (#2543)
- Plugin API: added 'message_compose_body' hook (#2520)
- Fix counters of all folders are checked in 'getunread' action with check_all_folders disabled (#2399)
- Fix displaying alternative parts in messages of type message/rfc822 (#2488)
- Fix possible messages exposure when using Roundcube behind a proxy (#2516)
- Fix unicode para and line separators in javascript response (#2542)
- Additional_message_headers: allow unsetting headers, support plugin's config file (#2505)
- Fix displaying of hidden directories in skins list (#2535)
- Fix open_basedir restriction error when reading skins list (#2537)
- Fix pasting from Office apps into html editor (#2508)
- Fix empty <a> tags parsing (#2509)
- Don't cut off attachment names when using non-RFC2231 encoding (#1912)
- Allow inserting signatures above replied message body (#991)
- Managesieve 2.0: multi-script support
- Fix imap_auth_type regression (#2502)
RELEASE 0.3.1
------------------
- Specify toolbar container in compose template (#2489)
- Fix $_SERVER['HTTPS'] check for SSL forcing on IIS (#2486)
- Avoid unnecessary page loads for selected tab (#2324)
- Fix quota indicator issues by content generation on client-size (#2454, #2470)
- Don't display disabled sections in Settings (#2380)
- Added server-side e-mail address validation with 'email_dns_check' option (#2175)
- Fix login page loading into an iframe when session expires (#2253)
- Allow setting port number in 'force_https' option (#2373)
- Option 'force_https' replaced by 'force_https' plugin
- Fix IE issue with non-UTF-8 characters in AJAX response (#2422)
- Partially fixed "empty body" issue by showing raw body of malformed message (#2427)
- Fix importing/sending to email address with whitespace (#2467)
- Added XIMSS (CommuniGate) driver for Password plugin
- Fix newly attached files are not saved in drafts w/o editing any text (#2457)
- Added attachment upload indicator with parallel upload (#2344)
- Use default_charset for bodies of messages without charset definition (#2446)
- Password: added cPanel driver
- Fix return to first page from e-mail screen (#2385)
- Fix handling HTML comments in HTML messages (#2448)
- Fix folder/messagelist controls alignment - icons used (#2356)
- Fix LDAP addressbook shows 'Contact not found' error sometimes (#2438)
- Fix cache status checking + improve cache operations performance (#2384)
- Prevent from setting INBOX as any of special folders (#2390)
- Fix regular expression for e-mail address (#2417)
- Fix Received header format
- Implemented sorting by message index - added 'index_sort' option (#2240)
- Fix dl() use in installer (#2415)
- Added 'ldap_debug' option
- Fix "Empty startup greeting" bug (#2369)
- Fix setting user name in 'new_user_identity' plugin (#2405)
- Fix incorrect count of new messages in folder list when using multiple IMAP clients (#2289)
- Fix all folders checking for new messages with disabled caching (#2399)
- Support skins in 'archive' and 'markasjunk' plugins
- Added 'html_editor' hook (#2353)
- Fix DB constraint violation when populating messages cache (#2338)
- Password: added password strength options (#2348)
- Fix LDAP partial result warning (#1928)
- Fix delete in message view deletes permanently with flag_for_deletion=true (#2382)
- Use faster/secure mt_rand() (#2376)
- Fix roundcube hangs on empty inbox with bincimapd (#2375)
- Fix wrong headers for IE on servers without $_SERVER['HTTPS'] (#2232)
- Force IE style headers for attachments in non-HTTPS session, 'use_https' option (#2023)
- Check 'post_max_size' for upload max filesize (#2372)
- Password Plugin: Fix %d inserts username instead of domain (#2371)
- Fix rcube_mdb2::affected_rows() (#2366)
RELEASE 0.3-stable
------------------
- Fix gn and givenName should be synonymous in LDAP addressbook (#2208)
- Add mail_domain to LDAP email entries without @ sign (#1652)
- Fix saving empty values in LDAP contact data (#2113)
- Fix LDAP contact update when RDN field is changed (#2119)
- Fix LDAP attributes case senitivity problems (#2155)
- Fix LDAP addressbook browsing when only one directory is used (#2314)
- Fix endless loop on error response for APPEND command (#2346)
- Don't require date.timezone setting in installer (#2284)
- Fix date sorting problem with Courier IMAP server (#2351)
- Unselect pressed buttons on mouse up (#2283)
- Don't set php_value error_log in .htaccess but mention in INSTALL (#2230)
- Fix too small status/flag/attachment columns in Safari 4 (#2349)
- Fix selection disabling while dragging splitter in webkit browsers (#2342)
- Added 'new_messages' plugin hook (#2298)
- Added 'logout_after' plugin hook (#2333)
- Added 'message_compose' hook
- Added 'imap_connect' hook (#2256)
- Fix vcard_attachments plugin (#2326)
- Updated PEAR::Auth_SASL to 1.0.3 version
- Use sequence names only with PostgreSQL (#2310)
- Re-designed User Preferences interface
- Fix MS SQL DDL (#2312)
- Fix rcube_mdb2.php: call to setCharset not implemented in mssql driver (#2311)
- Added 'display_next' option
- Fix rcube_mdb2::unixtimestamp for MS SQL (#2308)
- Fix HTML washing to respect character encoding
- Fix endless loop in iil_C_Login() with Courier IMAP (#2303)
- Fix #messagemenu display on IE (#2299)
- Speedup UI by using sprites for (toolbar) buttons
- Fix charset names with X- prefix handling
- Fix displaying of HTML messages with unknown/malformed tags (#2296)
RELEASE 0.3-RC1
---------------
- Fix import of vCard entries with params (#1857)
- Fix HTML messages output with empty block elements (#2271)
- Use request tokens to protect POST requests from CSRF [CVE-2009-4076, CVE-2009-4077]
- Added hook when killing a session
- Added hook to write_log function (#2268)
- Performance improvements by use UID commands (#2046)
- Fix HTML editor tabIndex setting (#2269)
- Added 'imap_debug' and 'smtp_debug' options
- Support strftime's format modifiers in date_* options (#1354)
- Support %h variable in 'smtp_server' option (#2101)
- Show SMTP errors in browser (#2233)
- Allow WBR tag in HTML message (#2259)
- Use spl_autoload_register() instead of __autoload (#2250)
- Add hook for identities listing (#2257)
- Trigger hook 'smtp_connect' when opening an SMTP connection (#2255)
- Added config option to enforce HTTPS connections
- Fix non-unicode characters caching in unicode database (#1209)
- Performance improvements of messages caching
- Fix empty Date header issue (#2229)
- Open collapsed folders during drag & drop (#2221)
- Fixed link text replacements (#2120)
- Also trigger 'insertrow' events on page load (#2151)
- No link on subject in IE browsers (#1438)
- Fixed filename encoding according to RFC2231 (#2192)
- Added message Edit feature (#727, #1101)
- Fix message Etag generation for counter issues (#1996)
- Fix messages searching on MailEnable IMAP (#2097)
- Fixed many 'skip_deleted' issues (#2006)
- 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 (#2205)
- Added possibility to invert messages selection
- After move/delete from 'show' action display next message instead of messages list (#2203)
- Fixed problem with double quote at the end of folder name (#2200)
- Speedup UI by using CSS sprites and etags/expires/deflate in Apache config (#1397,#2128)
- Support UID EXPUNGE: remove only moved/deleted messages
- Add drag cancelling with ESC key (#1036)
- Support initial identity name from virtuser_query (#807)
- Added message menu, removed Print and Source buttons
- Added possibility to save message as .eml file (#2178)
- Added 1 minute interval in autosave options (#2173)
- Support UTF-7 encoding in messages (#2156)
- Better support for malformed character names (#2093)
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 (#2122)
- Support UUencode content encoding (#2163)
- Minimize chance of race condition in session handling (#1260)
- Fix session handling on non-session SQL query error (#2078)
- Fix html editor mode setting when reopening draft message (#2158)
- Added quick search box menu (#1010)
- Fix wrong column sort order icons (#2149)
- Updated TinyMCE to 3.2.3 version
- Fix attachment names encoding when charset isn't specified in attachment part (#1483)
- Fix message normal priority problem (#2146)
- Fix autocomplete spinning wheel does not disappear (#2132)
- Added log_date_format option (#2060)
- Fix text wrapping in HTML editor after switching from plain text to HTML (#1917)
- Fix auto-complete function hangs with plus sign (#2141)
- Fix AJAX requests errors handler (#1503)
- Speed up message list displaying on IE
- Fix read/write database recognition (#2137)
RELEASE 0.2.2
-------------
- Fix quicksearchbox look in Chrome and Konqueror (#1380)
- Fix UTF-8 byte-order mark removing (#1911)
- Fix folders subscribtions on Konqueror (#1380)
- Fix debug console on Konqueror and Safari
- Fix messagelist focus issue when modifying status of selected messages (#2134)
- Support STARTTLS in IMAP connection (#1714)
- Fix DEL key problem in search boxes (#1923)
- Support several e-mail addresses per user from virtuser_file (#2036)
- Fix drag&drop with scrolling on IE (#2117)
- Fix adding signature separator in html mode (#1768)
- Fix opening attachment marks message as read (#2131)
- Fix 'temp_dir' does not support relative path under Windows (#1157)
- Fix "Initialize Database" button missing from installer (#2130)
- Fix compose window doesn't fit 1024x768 window (#1807)
- Fix service not available error when pressing back from compose dialog (#1942)
- Fix using mail() on Windows (#2111)
- Fix word wrapping in message-part's <PRE>s for printing (#2118)
- Fix incorrect word wrapping in outgoing plaintext multibyte messages (#2062)
- Fix double footer in HTML message with embedded images
- Fix TNEF implementation bug (#2107)
- Fix incorrect row id parsing for LDAP contacts list (#2116)
- Fix 'mode' parameter in sqlite DSN (#2106)
RELEASE 0.2.1
------------------
- Use US-ASCII as failover when Unicode searching fails (#2097)
- Fix errors handling in IMAP command continuations (#2097)
- Fix FETCH result parsing for servers returning flags at the end of result (#2098)
- Fix datetime columns defaults in mysql's DDL (#2012)
- Fix attaching more than nine inline images (#2094)
- Support 'UNICODE-1-1-UTF-7' alias for UTF-7 encoding (#2093)
- Fix mime-type detection using a hard-coded map (#1735)
- Don't return empty string if charset conversion failed (#2092)
- Disable concurrent autocomplete query results display (#2082)
- Fix new lines stripped from message footer (#2088)
- Fix IE problem with mouse click autocomplete (#2080)
- Fix html body washing on reply/forward + fix attachments handling (#2034)
- Fix multiple recipients input parsing (#2077)
- Fix replying to message with html attachment (#2034)
- Use default_charset for messages without specified charset (#2027, #1484961)
- Support non-standard "GMT-XXXX" literal in date header (#2074)
- Added TNEF support to decode MS Outlook attachments (winmail.dat)
- Fix "value continuation" MIME headers by adding required semicolon (#2073)
- Fix pressing select all/unread multiple times (#2069)
- Fix selecting all unread does not honor new messages (#2070)
- Fix some base64 encoded attachments handling (#2071)
- Support NGINX as IMAP backend: better BAD response handling (#2066)
- Performance fix: don't fetch attachment parts headers twice to parse filename
- Fix checking for recent messages on various IMAP servers (#2055)
- Performance fix: Don't fetch quota and recent messages in "message view" mode
- Fix displaying of alternative-inside-alternative messages (#2061)
- Fix MDNSent flag checking, use arbitrary keywords (asterisk) flag (#2059)
- Fix creation of folders with '&' sign in name
- Fix parsing of email addresses without angle brackets (#2048)
- Save spellcheck corrections when switching from plain to html editor (and spellchecking is on)
- Fix large search results on server without SORT capability (#2031)
- Get rid of preg_replace() with eval modifier and create_function usage (#2042)
- Bring back <base> and <link> tags in HTML messages
- Fix XSS vulnerability through background attributes [CVE-2009-0413]
- Fix problems with backslash as IMAP hierarchy delimiter (#1116)
- Secure vcard export by getting rid of preg's 'e' modifier use (#2045)
- Fix authentication when submitting form with existing session (#2037)
- Allow absolute URLs to images in HTML messages/sigs (#2029)
- Fix message body which contains both inline attachments and emotions
- Fix SQL query execution errors handling in rcube_mdb2 class (#1907)
- Fix address names with '@' sign handling (#2022)
- Improve messages display performance
- Fix messages searching with 'to:' modifier
RELEASE 0.2-STABLE
------------------
- Fix mark popup in IE 7 (#1785)
- Fix line-break issue when copy & paste in Firefox (#1832)
- Fix autocomplete "unknown server error" (#2008)
- Fix STARTTLS before AUTH in SMTP connection (#1415)
- Support multiple quota values in QUOTAROOT resonse (#1999)
- Only abbreviate file name for IE < 7 browsers (#1548)
- Performance: allow setting imap rootdir and delimiter before connect (#1628)
- Fix sorting of folders with more than 2 levels (#1953)
- Fix search results page jumps in LDAP addressbook (#1689)
- Fix empty line before the signature in IE (#1769)
- Fix horizontal scrollbar in preview pane on IE (#1228)
- Add Robots meta tag in login page and installer (#1385)
- Added 'show_images' option, removed 'addrbook_show_images' (#1977)
- Option to check for new mails in all folders (#1053)
- Don't set client busy when checking for new messages (#1706)
- Allow UTF-8 folder names in config (#1960)
- Add junk_mbox option configuration in installer (#1960)
- Do serverside addressbook queries for autocompletion (#1925)
- Allow setting attachment col position in 'list_cols' option
- Allow override 'list_cols' via skin (#1958)
- Fix 'cache' table cleanup on session destroy (#1913)
- Increase speed of session destroy and garbage clean up
- Fix session timeout when DB server got clock skew (#1890)
- Fix handling of some malformed messages (#1099)
- Speed up raw message body handling
- Better HTML entities conversion in html2text (#1916)
- Fix big memory consumption and speed up searching on servers without SORT capability
- Fix setting locale to tr_TR, ku and az_AZ (#1872)
- Use SORT for searching on servers with SORT capability
- Added message status filter
- Fix empty file sending (#1801)
- Improved searching with many criterias (calling one SEARCH command)
- Fix HTML editor initialization on IE (#1731)
- Add warning when switching editor mode from html to plain (#1888)
- Make identities list scrollable (#1930)
- Fix problem with numeric folder names (#1922)
- Added BYE response simple support to prevent from endless loops in imap.inc (#777)
- Fix unread message unintentionally marked as read if read_when_deleted=true (#1819)
- Remove port number from SERVER_NAME in smtp_helo_host (#1915)
- Don't send disposition notification receipts for messages marked as 'read' (#1918)
- Added 'keep_alive' and 'min_keep_alive' options (#1777)
- Added option 'identities_level', removed 'multiple_identities'
- Allow deleting identities when multiple_identities=false (#1840)
- Added option focus_on_new_message (#1789)
- Fix html2text class autoloading on Windows (#1904)
- Fix html signature formatting when identity save error occurred (#1833)
- Add feedback and set busy when moving folder (#1897)
- Fix 'Empty' link visibility for some languages e.g. Slovak (#1889)
- Fix messages count bar overlapping (#1703)
- Fix adding signature in drafts compose mode (#1884)
- Fix iil_C_Sort() to support very long and/or divided responses (#1713)
- Fix matching case sensitivity when setting identity on reply (#1881)
- Prefer default identity on reply
- Fix imap searching on ISMail server (#1870)
- Add css class for flagged messages (#1868)
- Write username instead of id in sendmail log (#1879)
- Fix htmlspecialchars() use for PHP version < 5.2.3 (#1877)
- Fix js keywords escaping in json_serialize() for IE/Opera (#1874)
- Added bin/killcache.php script (#1839)
- Add support for SJIS, GB2312, BIG5 in rc_detect_encoding()
- Fix vCard file encoding detection for non-UTF-8 strings (#1820)
- Add 'skip_deleted' option in User Preferences (#1850)
- Minimize "inline" javascript scripts use (#1838)
- Fix css class setting for folders with names matching defined classes names (#1772)
- Fix race conditions when changing mailbox
- Fix spellchecking when switching to html editor (#1779)
- Fix compose window width/height (#1807)
- Allow calling msgimport.sh/msgexport.sh from any directory (#1837)
- Localized filesize units (#1760)
- Better handling of "no identity" and "no email in identity" situations (#1592)
- Added 'mime_param_folding' option with possibility to choose long/non-ascii attachment names encoding eg. to be readable in MS Outlook/OE (#1743)
- Added "advanced options" feature in User Preferences
- Fix unread counter when displaying cached massage in preview panel (#1720)
- Fix htmleditor spellchecking on MS Windows (#1808)
- Fix problem with non-ascii attachment names in Mail_mime (#1700, #1576)
- Fix language autodetection (#1812)
- Fix button label in folders management (#1816)
- Fix collapsed folder not indicating unread msgs count of all subfolders (#1814)
- Fix handling of apostrophes in filenames decoded according to rfc2231
RELEASE 0.2-BETA
----------------
- Made config files location configurable (#1664)
- Reduced memory footprint when forwarding attachments (#1764)
- Allow and use spellcheck attribute for input/textarea fields (#1545)
- Added icons for forwarded/forwarded+replied messages (#1691)
- Added Reply-To to forwarded emails (#1739)
- Display progress message for folders create/delete/rename (#1774)
- Smart Tags and NOBR tag support in html messages (#1780, #1748)
- Redesign of the identities settings (#836)
- Add config option to disable creation/deletion of identities (#1139)
- Added 'sendmail_delay' option to restrict messages sending interval (#1135)
- Added vertical splitter for folders list resizing
- Added possibility to view all headers in message view
- Fixed splitter drag/resize on Opera (#1626)
- Fixed quota img height/width setting from template (#1396)
- Refactor drag & drop functionality. Don't rely on browser events anymore (#1108)
- Insert "virtual" folders in subscription list (#1333)
- Added link to open message in new window
- Enable export of address book contacts as vCard
- Add feature to import contacts from vcard files (#395)
- Respect Content-Location headers in multipart/related messages according to RFC2110 (#1464)
- Allowed max. attachment size now indicated in compose screen (#1523)
- Also capture backspace key in list mode (#1186)
- Allow application/pgp parts to be displayed (#1309)
- Correctly handle options in mailto-links (#1671)
- Immediately save sort_col/sort_order in user prefs (#1698)
- Truncate very long (above 50 characters) attachment filenames when displaying
- Allow to auto-detect client language if none set (#1095)
- 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 (#1738)
- Mark form buttons that provide the most obvious operation (mainaction)
- Added option 'quota_zero_as_unlimited' (#1206)
- Added PRE handling in html2text class (#1301)
- Added folder hierarchy collapsing
- Added options to use syslog instead of log file (#1389)
- Added Logging & Debugging section in Installer
- Fix In-Reply-To and References headers when composing saved draft message (#1718)
- Fix html message charset conversion for charsets with underline (#1717)
- Fix buttons status after contacts deletion (#1675)
- Fix escaping of To: and From: fields when building message body for reply or forward in the HTML editor (#1432)
- Use current mailbox name in template (#1690)
- Better fix for skipping untagged responses (#1694)
- Added pspell support patch by Kris Steinhoff (#781)
- Enable spellchecker for HTML editor (#1589)
- Respect spellcheck_uri in tinyMCE spellchecker (#941)
- Case insensitive contacts searching using PostgreSQL (#1692)
- Make default imap folders configurable for each user (#1558)
- Save outgoing mail to selectable folder (#1324581)
- Fix hiding of mark menu when clicking th button again (#1463)
- Use long date format in print mode (#1643)
- Updated TinyMCE to version 3.1.0.1
- Re-enable autocomplete attribute for login form (#1661)
- Check PERMANENTFLAGS before saving $MDNSent flag (#1478, #1485163)
- Added flag column on messages list (#1220)
- Patched Mail/MimePart.php (http://pear.php.net/bugs/bug.php?id=14232)
- Allow trash/junk subfolders to be purged (#1568)
- Store compose parameters in session and redirect to a unique URL
- Fixed CRAM-MD5 authentication (#1364)
- Fixed forwarding messages with one HTML attachment (#1103)
- Fixed encoding of message/rfc822 attachments and image/pjpeg handling (#1439)
- 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 (#1204)
- User preferences grouped in more fieldsets
- Fix corrupted MIME headers of messages in Sent folder (#1587)
- Fixed bug in MDB2 package: http://pear.php.net/bugs/bug.php?id=14124
- Use keypress instead of keydown to select list's row (#1362)
- Don't call expunge and don't remove message row after message move if flag_for_deletion is set to true (#1505)
RELEASE 0.2-ALPHA
-----------------
- Added option to disable autocompletion from selected LDAP address books (#1445)
- TLS support in LDAP connections: 'use_tls' property (#1581)
- Fixed removing messages from search set after deleting them (#1583)
- imap.inc: Fixed iil_C_FetchStructureString() to handle many
literal strings in response (#1483)
- Support for subfolders in default/protected folders (#1250)
- Disallowed delimiter in folder name (#1351)
- Support " and \ in folder names
- Escape \ in login (#1214)
- Better HTML sanitization with the DOM-based washtml script (#1276)
- Fixed sorting of folders with non-ascii characters
- Fixed Mysql DDL for default identities creation (#1554)
- In Preferences added possibility to configure 'read_when_deleted',
'mdn_requests', 'flag_for_deletion' options
- Made IMAP auth type configurable (#683)
- Fixed empty values with FROM_UNIXTIME() in rcube_mdb2 (#1540)
- Fixed attachment list on IE 6/7 (#1355)
- Fixed JavaScript in compose.html that shows cc/bcc fields if populated
- Make password input fields of type password in installer (#1417)
- Don't attempt to delete cache entries if enable_caching is FALSE (#1537)
- Optimized messages sorting on servers without sort capability (#1535)
- Corrected message headers decoding when charset isn't specified and improved
support for native languages (#1536, #1534)
- Expanded LDAP configuration options to support LDAP server writes.
- Installer: encode special characters in DB username/password (#1529)
- Fixed management of folders with national characters in names (#1526, #1504)
- Fixed identities saving when using MDB2 pgsql driver (#1525)
- Fixed BCC header reset (#1501)
- Improved messages list performance - patch from Justin Heesemann
- Append skin_path to images location only when it starts with '/' sign (#1398)
- Fix IMAP response in message body when message has no body (#1479)
- Fixed non-RFC dates formatting (#1429)
- Fixed typo in set_charset() (#1498)
- Decode entities when inserting HTML signature to plain text message (#1497)
- HTML editing is now working with PHP5 updates and TinyMCE v3.0.6
- Fixed signature loading on Windows (#1169)
- Added language support to HTML editing (#1401)
- Fixed remove signature when replying (#446)
- Fixed problem with line with a space at the end (#1440)
- Fixed <!DOCTYPE> tag filtering (#1066)
- Fixed <?xml> tag filtering (#1075)
- Added sections (fieldset+label) in Settings interface
- Mark as read in one action with message preview (#1486)
- Deleted redundant quota reads (#1486)
- Added options for empty trash and expunge inbox on logout (#707)
- 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 (#1461)
- Remove hard-coded image size in skin templates (#1423)
- Database schema improvements (dropped unnecessary indexes)
- Fixed creating a new folder with a comma in its name (#1263)
- Fixed sorting of messages when default mailbox is empty (#1020)
- Improve message previewpane - less loading (#1019)
- Fixed login form autoompletion (#1378)
- Fixed virtuser_query option for mdb2 backend (#1409)
- Fixed attachment resoting from Drafts when message body was empty (#1144)
- Fixed usage of ob_gzhandler (#1390)
- Fixed message part window in IE6 (#1211)
- Fixed decoding of mime-encoded strings (#938)
- Fixed some iconv/mb_string problems (#1202)
- Correctly quote mailbox name when using in URL (#1016)
- Fixed "headers already sent" errors (#1399)
RELEASE 0.1-STABLE
------------------
- Added interactive installer script
- Fix folder adding/renaming inspired by #1349
- Localize folder name in page title (#1338)
- Fix code using wrong variable name (#818)
- Allow to send mail with BCC recipients only
- condense TinyMCE toolbar down to one line, removing table buttons (#1306)
- Add function to mark the selected messages as read/unread (#641)
- Also do charset decoding as suggested in RFC 2231 (fix #1022)
- Show message count in folder list and hint when creating a subfolder
- Distinguish ssl and tls for imap connections (#1252)
- Added some charset aliases to fix typical mis-labelling (#1185)
- Remember decision to display images for a certain message during session (#1310)
- Truncate attachment filenames to 55 characters due to an IE bug (#1313)
- Make sending of read receipts configurable
- Respect config when localize folder names (#1280)
- Also respect receipt and priority settings when re-opening a draft message
- Remember search results (closes #722), 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 (#1179)
- Implement Message-Disposition-Notification (Receipts)
- Fix overriding of session vars when register_globals is on (#1255)
- Fix bug with case-sensitive folder names (#973)
- 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 (#925)
- Switch to/from when searcing in Sent folder (#1177)
- Correctly read the References header (#1236)
- Unset old cookie before sending a new value (#1232)
- Correctly decode attachments when downloading them (#1235 and #1484642)
- Suppress IE errors when clearing attachments form (#1043)
- Log error when login fails due to auto_create_user turned off
- Filter linked/imported CSS files (closes #844)
- Improve message compose screen (closes #1060)
- Select next row after removing one from list (#1063)
RELEASE 0.1-RC2
---------------
- Enable drag-&-dropping of folders to a new parent and allow to create subfolders (#637)
- Suppress IE errors when clearing attachments form (#1043)
- Set preferences field in user table to NULL (#1062)
- Log error when login fails due to auto_create_user turned off
- Filter linked/imported CSS files (closes #844)
- Improve message compose screen (closes #1060)
- Select next row after removing one from list (#1063)
- Make smtp HELO/EHLO hostname configurable (#851)
- IPv6 Compatibility (#1023), Patch #1484373
- Unlock interface when message sending fails (#1188)
- Eval PHP code in template includes (if configured)
- Show message when folder is empty. Mo more static text in table (#1068)
- Only display unread count in page title when new messages arrived
- Fixed wrong delete button tooltip (#785)
- Fixed charset encoding bug (#1091)
- Applied patch for LDAP version (#1175)
- Improved XHTML validation
- Fix message list selection (#1174)
- Better fix lowercased usernames (#1120)
- Update pngbehavior Script as suggested in #1134
- 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 (#1074)
- Add alternative for getallheaders() (fix #1146)
- Identify mailboxes case-sensitive
- Sort mailbox list case-insensitive (closes #1032)
- Fix display of multipart messages from Apple Mail (closes #823)
- 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 #1089)
- Added //IGNORE to iconv call (patch #1086, closes #821)
- Check if mbstring supports charset (#1003 and #1004)
- Prefer iconv over mbstring (as suggested in #1004)
- Check filesize of template includes (#1079)
- Fixed bug with buttons not dimming/enabling properly after switching folders
- Fixed compose window becoming unresponsive after saving a draft (#1132)
- Re-enabled "Back" button in compose window now that bug #1132 is fixed
- Fixed unresponsive interface issue when downloading attachments (#1138)
- 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 (#1140)
- Fix status message bug #1114 with regard to #1041
- 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 #1056)
- Prevent default events on subject links (#1071)
- Use HTTP-POST requests for actions that change state
RELEASE 0.1-RC1
---------------
- Use global filters and bind username/ for Ldap searches (#909)
- Hide quota display if imap server does not support it
- Hide address groups if no LDAP servers configured
- Add link to message subjects (closes #982)
- Better SQL query for contact listing/search (closes #1051)
- Fixed marking as read in preview pane (closes #1048)
- CSS hack to display attachments correctly in IE6
- Wrap message body text (closes #901)
- LDAP access is back in address book (closes #864)
- Added search function for contacts
- New Template parsing and output encoding
- Fixed bugs #884 and #793
- Fixed message moving procedure (closes #1013)
- Fixed display of multiple attachments (closes #647)
- Fixed check for new messages (closes #1015)
- List attachments without filename
- New session authentication: Change sessid cookie when login, authentication with sessauth cookie is now configurable.
Should close bugs #774 and #1484299
- Correctly translate mailbox names (closes #993)
- Quote e-mail address links (closes #1007)
- 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 #838)
- Also use user_id for unique key in messages table (closes #857)
- Hide contacts drop down on blur (closes #946)
- 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 #943)
- Fixed bug in Postgres DB handling (closes #852)
- Fixed bug of invalid calls to fetchRow() in rcube_db.inc (closes #996)
- Fixed array_merge bug (closes #997)
- Fixed flag for deletion in list view (closes #987)
- Finally support semicolons as recipient separator (closes ##976)
- Fixed message headers (subject) encoding
- check if safe mode is on or not (closes #990)
- Show "no subject" in message list if subject is missing (closes #971)
- Solved page caching of message preview (closes #905)
- Only use gzip compression if configured (closes #967)
- Fixed priority selector issue (#903)
- Fixed some CSS issues in default skin (closes #951 and #911)
- Prevent from double quoting of numeric HTML character references (closes #978)
- Fixed display of HTML message attachments (closes #927)
- Applied patch for preview caching (closes #933)
- Added error handling for attachment uploads
- Use multibyte safe string functions where necessary (closes #798)
- 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 #890)
- Show remote images when opening HTML message part as attachment
- Improve memory usage when sending mail (closes #871)
- Mark messages as read once the preview is loaded (closes #1484132)
- Include smtp final response in log (closes #862)
- Corrected date string in sent message header (closes #887)
- Correclty choose "To" column in sent and draft mailboxes (closes #769)
- Changed srong tooltips for message browse buttons (closes #757)
- Fixed signature delimiter character to be standard (Bug #830)
- Fixed XSS vulnerability (Bug #877)
- Remove newlines from mail headers (Bug #827)
- Selection issues when moving/deleting (Bug #837)
- Applied patch of Clement Moulin for imap host auto-selection
- ISO-encode IMAP password for plaintext login (Bugs #792 & #723)
- Fixed folder name encoding in subscription list (Bug #879)
- Fixed JS errors in identity list (Bug #885)
- Translate foldernames in folder form (closes #879)
- 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 #817)
- Enable contact editing right after creation (Bug #644)
- Correct UTF-7 to UTF-8 conversion if mbstring is not available
- Fixed IMAP fetch of message body (Bug #819)
- Fixed safe_mode problems (Bug #539)
- Fixed wrong header encoding (Bug #1483976)
- Made automatic draft saving configurable
- Fixed JS bug when renaming folders (Bug #799)
- 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 #616)
- Fixed saving of contact into MySQL from LDAP query results (Ticket #681)
- Fixed folder renaming: unsubscribe before rename (Bug #750)
- Finalized new message parsing (+ chaching)
- Fixed wrong usage of mbstring (Bug #645)
- Set default spelling language (Ticket #764)
- Added support for Nox Spell Server
- Re-built message parsing (Bug #422)
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/config/defaults.inc.php b/config/defaults.inc.php
index 360d15638..312a7ec35 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
@@ -1,1267 +1,1264 @@
<?php
// ---------------------------------------------------------------------
// WARNING: Do not edit this file! Copy configuration to config.inc.php.
// ---------------------------------------------------------------------
/*
+-----------------------------------------------------------------------+
| Default settings for all configuration options |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2018, 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. |
+-----------------------------------------------------------------------+
*/
$config = array();
// ----------------------------------
// SQL DATABASE
// ----------------------------------
// Database connection string (DSN) for read+write operations
// Format (compatible with PEAR MDB2): db_provider://user:password@host/database
// Currently supported db_providers: mysql, pgsql, sqlite, mssql, sqlsrv, oracle
// For examples see http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php
// NOTE: for SQLite use absolute path (Linux): 'sqlite:////full/path/to/sqlite.db?mode=0646'
// or (Windows): 'sqlite:///C:/full/path/to/sqlite.db'
$config['db_dsnw'] = 'mysql://roundcube:@localhost/roundcubemail';
// Database DSN for read-only operations (if empty write database will be used)
// useful for database replication
$config['db_dsnr'] = '';
// Disable the use of already established dsnw connections for subsequent reads
$config['db_dsnw_noread'] = false;
// use persistent db-connections
// beware this will not "always" work as expected
// see: http://www.php.net/manual/en/features.persistent-connections.php
$config['db_persistent'] = false;
// you can define specific table (and sequence) names prefix
$config['db_prefix'] = '';
// Mapping of table names and connections to use for ALL operations.
// This can be used in a setup with replicated databases and a DB master
// where read/write access to cache tables should not go to master.
$config['db_table_dsn'] = array(
// 'cache' => 'r',
// 'cache_index' => 'r',
// 'cache_thread' => 'r',
// 'cache_messages' => 'r',
);
// It is possible to specify database variable values e.g. some limits here.
// Use them if your server is not MySQL or for better performance.
// For example Roundcube uses max_allowed_packet value (in bytes)
// which limits query size for database cache operations.
$config['db_max_allowed_packet'] = null;
// ----------------------------------
// LOGGING/DEBUGGING
// ----------------------------------
// log driver: 'syslog', 'stdout' or 'file'.
$config['log_driver'] = 'file';
// date format for log entries
// (read http://php.net/manual/en/function.date.php for all format characters)
$config['log_date_format'] = 'd-M-Y H:i:s O';
// length of the session ID to prepend each log line with
// set to 0 to avoid session IDs being logged.
$config['log_session_id'] = 8;
// Default extension used for log file name
$config['log_file_ext'] = '.log';
// Syslog ident string to use, if using the 'syslog' log driver.
$config['syslog_id'] = 'roundcube';
// Syslog facility to use, if using the 'syslog' log driver.
// For possible values see installer or http://php.net/manual/en/function.openlog.php
$config['syslog_facility'] = LOG_USER;
// Activate this option if logs should be written to per-user directories.
// Data will only be logged if a directory <log_dir>/<username>/ exists and is writable.
$config['per_user_logging'] = false;
// Log sent messages to <log_dir>/sendmail or to syslog
$config['smtp_log'] = true;
// Log successful/failed logins to <log_dir>/userlogins or to syslog
$config['log_logins'] = false;
// Log session authentication errors to <log_dir>/session or to syslog
$config['log_session'] = false;
// Log SQL queries to <log_dir>/sql or to syslog
$config['sql_debug'] = false;
// Log IMAP conversation to <log_dir>/imap or to syslog
$config['imap_debug'] = false;
// Log LDAP conversation to <log_dir>/ldap or to syslog
$config['ldap_debug'] = false;
// Log SMTP conversation to <log_dir>/smtp or to syslog
$config['smtp_debug'] = false;
// Log Memcache conversation to <log_dir>/memcache or to syslog
$config['memcache_debug'] = false;
// Log APC conversation to <log_dir>/apc or to syslog
$config['apc_debug'] = false;
// Log Redis conversation to <log_dir>/redis or to syslog
$config['redis_debug'] = false;
// ----------------------------------
// IMAP
// ----------------------------------
// The IMAP host chosen to perform the log-in.
// Leave blank to show a textbox at login, give a list of hosts
// to display a pulldown menu or set one host as string.
// To use SSL/TLS connection, enter hostname with prefix ssl:// or tls://
// Supported replacement variables:
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
// %s - domain name after the '@' from e-mail address provided at login screen
// For example %n = mail.domain.tld, %t = domain.tld
// WARNING: After hostname change update of mail_host column in users table is
// required to match old user data records with the new host.
$config['default_host'] = 'localhost';
// TCP port used for IMAP connections
$config['default_port'] = 143;
// IMAP authentication method (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or null).
// Use 'IMAP' to authenticate with IMAP LOGIN command.
// By default the most secure method (from supported) will be selected.
$config['imap_auth_type'] = null;
// IMAP socket context options
// See http://php.net/manual/en/context.ssl.php
// The example below enables server certificate validation
//$config['imap_conn_options'] = array(
// 'ssl' => array(
// 'verify_peer' => true,
// 'verify_depth' => 3,
// 'cafile' => '/etc/openssl/certs/ca.crt',
// ),
// );
// Note: These can be also specified as an array of options indexed by hostname
$config['imap_conn_options'] = null;
// IMAP connection timeout, in seconds. Default: 0 (use default_socket_timeout)
$config['imap_timeout'] = 0;
// Optional IMAP authentication identifier to be used as authorization proxy
$config['imap_auth_cid'] = null;
// Optional IMAP authentication password to be used for imap_auth_cid
$config['imap_auth_pw'] = null;
// If you know your imap's folder delimiter, you can specify it here.
// Otherwise it will be determined automatically
$config['imap_delimiter'] = null;
// If you know your imap's folder vendor, you can specify it here.
// Otherwise it will be determined automatically. Use lower-case
// identifiers, e.g. 'dovecot', 'cyrus', 'gmail', 'hmail', 'uw-imap'.
$config['imap_vendor'] = null;
// If IMAP server doesn't support NAMESPACE extension, but you're
// using shared folders or personal root folder is non-empty, you'll need to
// set these options. All can be strings or arrays of strings.
// Note: Folders need to be ended with directory separator, e.g. "INBOX."
// (special directory "~" is an exception to this rule)
// Note: These can be used also to overwrite server's namespaces
// Note: Set these to FALSE to disable access to specified namespace
$config['imap_ns_personal'] = null;
$config['imap_ns_other'] = null;
$config['imap_ns_shared'] = null;
// By default IMAP capabilities are readed after connection to IMAP server
// In some cases, e.g. when using IMAP proxy, there's a need to refresh the list
// after login. Set to True if you've got this case.
$config['imap_force_caps'] = false;
// By default list of subscribed folders is determined using LIST-EXTENDED
// extension if available. Some servers (dovecot 1.x) returns wrong results
// for shared namespaces in this case. https://github.com/roundcube/roundcubemail/issues/2474
// Enable this option to force LSUB command usage instead.
// Deprecated: Use imap_disabled_caps = array('LIST-EXTENDED')
$config['imap_force_lsub'] = false;
// Some server configurations (e.g. Courier) doesn't list folders in all namespaces
// Enable this option to force listing of folders in all namespaces
$config['imap_force_ns'] = false;
// Some servers return hidden folders (name starting witha dot)
// from user home directory. IMAP RFC does not forbid that.
// Enable this option to hide them and disable possibility to create such.
$config['imap_skip_hidden_folders'] = false;
// Some servers do not support folders with both folders and messages inside
// If your server supports that use true, if it does not, use false.
// By default it will be determined automatically (once per user session).
$config['imap_dual_use_folders'] = null;
// List of disabled imap extensions.
// Use if your IMAP server has broken implementation of some feature
// and you can't remove it from CAPABILITY string on server-side.
// For example UW-IMAP server has broken ESEARCH.
// Note: Because the list is cached, re-login is required after change.
$config['imap_disabled_caps'] = array();
// Log IMAP session identifiers after each IMAP login.
// This is used to relate IMAP session with Roundcube user sessions
$config['imap_log_session'] = false;
// Type of IMAP indexes cache. Supported values: 'db', 'apc' and 'memcache'.
$config['imap_cache'] = null;
// Enables messages cache. Only 'db' cache is supported.
// This requires an IMAP server that supports QRESYNC and CONDSTORE
// extensions (RFC7162). See synchronize() in program/lib/Roundcube/rcube_imap_cache.php
// for further info, or if you experience syncing problems.
$config['messages_cache'] = false;
// Lifetime of IMAP indexes cache. Possible units: s, m, h, d, w
$config['imap_cache_ttl'] = '10d';
// Lifetime of messages cache. Possible units: s, m, h, d, w
$config['messages_cache_ttl'] = '10d';
// Maximum cached message size in kilobytes.
// Note: On MySQL this should be less than (max_allowed_packet - 30%)
$config['messages_cache_threshold'] = 50;
// ----------------------------------
// SMTP
// ----------------------------------
// SMTP server host (for sending mails).
// Enter hostname with prefix tls:// to use STARTTLS, or use
// prefix ssl:// to use the deprecated SSL over SMTP (aka SMTPS)
// Supported replacement variables:
// %h - user's IMAP hostname
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
// %z - IMAP domain (IMAP hostname without the first part)
// For example %n = mail.domain.tld, %t = domain.tld
$config['smtp_server'] = 'localhost';
// SMTP port (default is 587)
$config['smtp_port'] = 587;
// SMTP username (if required) if you use %u as the username Roundcube
// will use the current username for login
$config['smtp_user'] = '%u';
// SMTP password (if required) if you use %p as the password Roundcube
// will use the current user's password for login
$config['smtp_pass'] = '%p';
// SMTP AUTH type (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or empty to use
// best server supported one)
$config['smtp_auth_type'] = null;
// Optional SMTP authentication identifier to be used as authorization proxy
$config['smtp_auth_cid'] = null;
// Optional SMTP authentication password to be used for smtp_auth_cid
$config['smtp_auth_pw'] = null;
// SMTP HELO host
// Hostname to give to the remote server for SMTP 'HELO' or 'EHLO' messages
// Leave this blank and you will get the server variable 'server_name' or
// localhost if that isn't defined.
$config['smtp_helo_host'] = '';
// SMTP connection timeout, in seconds. Default: 0 (use default_socket_timeout)
// Note: There's a known issue where using ssl connection with
// timeout > 0 causes connection errors (https://bugs.php.net/bug.php?id=54511)
$config['smtp_timeout'] = 0;
// SMTP socket context options
// See http://php.net/manual/en/context.ssl.php
// The example below enables server certificate validation, and
// requires 'smtp_timeout' to be non zero.
// $config['smtp_conn_options'] = array(
// 'ssl' => array(
// 'verify_peer' => true,
// 'verify_depth' => 3,
// 'cafile' => '/etc/openssl/certs/ca.crt',
// ),
// );
// Note: These can be also specified as an array of options indexed by hostname
$config['smtp_conn_options'] = null;
// ----------------------------------
// LDAP
// ----------------------------------
// Type of LDAP cache. Supported values: 'db', 'apc' and 'memcache'.
$config['ldap_cache'] = 'db';
// Lifetime of LDAP cache. Possible units: s, m, h, d, w
$config['ldap_cache_ttl'] = '10m';
// ----------------------------------
// CACHE(S)
// ----------------------------------
// Use these hosts for accessing memcached
// Define any number of hosts in the form of hostname:port or unix:///path/to/socket.file
$config['memcache_hosts'] = null; // e.g. array( 'localhost:11211', '192.168.1.12:11211', 'unix:///var/tmp/memcached.sock' );
// Controls the use of a persistent connections to memcache servers
// See http://php.net/manual/en/memcache.addserver.php
$config['memcache_pconnect'] = true;
// Value in seconds which will be used for connecting to the daemon
// See http://php.net/manual/en/memcache.addserver.php
$config['memcache_timeout'] = 1;
// Controls how often a failed server will be retried (value in seconds).
// Setting this parameter to -1 disables automatic retry.
// See http://php.net/manual/en/memcache.addserver.php
$config['memcache_retry_interval'] = 15;
// use these hosts for accessing Redis.
// Currently only one host is supported. cluster support may come in a future release.
// You can pass 4 fields, host, port, database and password.
// Unset fields will be set to the default values host=127.0.0.1, port=6379, database=0, password= (empty)
$config['redis_hosts'] = null; // e.g. array( 'localhost:6379' ); array( '192.168.1.1:6379:1:secret' );
// Maximum size of an object in memcache (in bytes). Default: 2MB
$config['memcache_max_allowed_packet'] = '2M';
// Maximum size of an object in APC cache (in bytes). Default: 2MB
$config['apc_max_allowed_packet'] = '2M';
// Maximum size of an object in Redis cache (in bytes). Default: 2MB
$config['redis_max_allowed_packet'] = '2M';
// ----------------------------------
// SYSTEM
// ----------------------------------
// THIS OPTION WILL ALLOW THE INSTALLER TO RUN AND CAN EXPOSE SENSITIVE CONFIG DATA.
// ONLY ENABLE IT IF YOU'RE REALLY SURE WHAT YOU'RE DOING!
$config['enable_installer'] = false;
// don't allow these settings to be overridden by the user
$config['dont_override'] = array();
// List of disabled UI elements/actions
$config['disabled_actions'] = array();
// define which settings should be listed under the 'advanced' block
// which is hidden by default
$config['advanced_prefs'] = array();
// provide an URL where a user can get support for this Roundcube installation
// PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!
$config['support_url'] = '';
// replace Roundcube logo with this image
// specify an URL relative to the document root of this Roundcube installation
// an array can be used to specify different logos for specific template files
// '*' for default logo
// ':favicon' for favicon
// ':print' for logo on all print templates (e.g. messageprint, contactprint)
// ':small' for small screen logo in Elastic
// different logos can be specified for different skins by prefixing the skin name to the array key
// config applied in order: <skin>:<template>, <skin>:*, <template>, *
// for example array("*" => "/images/roundcube_logo.png", "messageprint" => "/images/roundcube_logo_print.png", "elastic:*" => "/images/logo.png")
$config['skin_logo'] = null;
// automatically create a new Roundcube user when log-in the first time.
// a new user will be created once the IMAP login succeeds.
// set to false if only registered users can use this service
$config['auto_create_user'] = true;
// Enables possibility to log in using email address from user identities
$config['user_aliases'] = false;
// use this folder to store log files
// must be writeable for the user who runs PHP process (Apache user if mod_php is being used)
// This is used by the 'file' log driver.
$config['log_dir'] = RCUBE_INSTALL_PATH . 'logs/';
// use this folder to store temp files
// must be writeable for the user who runs PHP process (Apache user if mod_php is being used)
$config['temp_dir'] = RCUBE_INSTALL_PATH . 'temp/';
// expire files in temp_dir after 48 hours
// possible units: s, m, h, d, w
$config['temp_dir_ttl'] = '48h';
// Enforce connections over https
// With this option enabled, all non-secure connections will be redirected.
// It can be also a port number, hostname or hostname:port if they are
// different than default HTTP_HOST:443
$config['force_https'] = false;
// tell PHP that it should work as under secure connection
// even if it doesn't recognize it as secure ($_SERVER['HTTPS'] is not set)
// e.g. when you're running Roundcube behind a https proxy
// this option is mutually exclusive to 'force_https' and only either one of them should be set to true.
$config['use_https'] = false;
// Allow browser-autocompletion on login form.
// 0 - disabled, 1 - username and host only, 2 - username, host, password
$config['login_autocomplete'] = 0;
// Forces conversion of logins to lower case.
// 0 - disabled, 1 - only domain part, 2 - domain and local part.
// If users authentication is case-insensitive this must be enabled.
// Note: After enabling it all user records need to be updated, e.g. with query:
// UPDATE users SET username = LOWER(username);
$config['login_lc'] = 2;
// Maximum length (in bytes) of logon username and password.
$config['login_username_maxlen'] = 1024;
$config['login_password_maxlen'] = 1024;
// Logon username filter. Regular expression for use with preg_match().
// Example: '/^[a-z0-9_@.-]+$/'
$config['login_username_filter'] = null;
// Brute-force attacks prevention.
// The value specifies maximum number of failed logon attempts per minute.
$config['login_rate_limit'] = 3;
// Includes should be interpreted as PHP files
$config['skin_include_php'] = false;
// display product name and software version on login screen
// 0 - hide product name and version number, 1 - show product name only, 2 - show product name and version number
$config['display_product_info'] = 1;
// Session lifetime in minutes
$config['session_lifetime'] = 10;
// Session domain: .example.org
$config['session_domain'] = '';
// Session name. Default: 'roundcube_sessid'
$config['session_name'] = null;
// Session authentication cookie name. Default: 'roundcube_sessauth'
$config['session_auth_name'] = null;
// Session path. Defaults to PHP session.cookie_path setting.
$config['session_path'] = null;
// Backend to use for session storage. Can either be 'db' (default), 'redis', 'memcache', or 'php'
//
// If set to 'memcache', a list of servers need to be specified in 'memcache_hosts'
// Make sure the Memcache extension (http://pecl.php.net/package/memcache) version >= 2.0.0 is installed
//
// If set to 'redis', a server needs to be specified in 'redis_hosts'
// Make sure the Redis extension (http://pecl.php.net/package/redis) version >= 2.0.0 is installed
//
// Setting this value to 'php' will use the default session save handler configured in PHP
$config['session_storage'] = 'db';
// List of trusted proxies
// X_FORWARDED_* and X_REAL_IP headers are only accepted from these IPs
$config['proxy_whitelist'] = array();
// List of trusted host names
// Attackers can modify Host header of the HTTP request causing $_SERVER['SERVER_NAME']
// or $_SERVER['HTTP_HOST'] variables pointing to a different host, that could be used
// to collect user names and passwords. Some server configurations prevent that, but not all.
// An empty list accepts any host name. The list can contain host names
// or PCRE patterns (without // delimiters, that will be added automatically).
$config['trusted_host_patterns'] = array();
// check client IP in session authorization
$config['ip_check'] = false;
-// check referer of incoming requests
-$config['referer_check'] = false;
-
// X-Frame-Options HTTP header value sent to prevent from Clickjacking.
// Possible values: sameorigin|deny|allow-from <uri>.
// Set to false in order to disable sending the header.
$config['x_frame_options'] = 'sameorigin';
// This key is used for encrypting purposes, like storing of imap password
// in the session. For historical reasons it's called DES_key, but it's used
// with any configured cipher_method (see below).
$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
// Encryption algorithm. You can use any method supported by openssl.
// Default is set for backward compatibility to DES-EDE3-CBC,
// but you can choose e.g. AES-256-CBC which we consider a better choice.
$config['cipher_method'] = 'DES-EDE3-CBC';
// Automatically add this domain to user names for login
// Only for IMAP servers that require full e-mail addresses for login
// Specify an array with 'host' => 'domain' values to support multiple hosts
// Supported replacement variables:
// %h - user's IMAP hostname
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
// %z - IMAP domain (IMAP hostname without the first part)
// For example %n = mail.domain.tld, %t = domain.tld
$config['username_domain'] = '';
// Force domain configured in username_domain to be used for login.
// Any domain in username will be replaced by username_domain.
$config['username_domain_forced'] = false;
// This domain will be used to form e-mail addresses of new users
// Specify an array with 'host' => 'domain' values to support multiple hosts
// Supported replacement variables:
// %h - user's IMAP hostname
// %n - http hostname ($_SERVER['SERVER_NAME'])
// %d - domain (http hostname without the first part)
// %z - IMAP domain (IMAP hostname without the first part)
// For example %n = mail.domain.tld, %t = domain.tld
$config['mail_domain'] = '';
// Password character set.
// If your authentication backend supports it, use "UTF-8".
// Otherwise, use the appropriate character set.
// Defaults to ISO-8859-1 for backward compatibility.
$config['password_charset'] = 'ISO-8859-1';
// How many seconds must pass between emails sent by a user
$config['sendmail_delay'] = 0;
// Message size limit. Note that SMTP server(s) may use a different value.
// This limit is verified when user attaches files to a composed message.
// Size in bytes (possible unit suffix: K, M, G)
$config['max_message_size'] = '100M';
// Maximum number of recipients per message (including To, Cc, Bcc).
// Default: 0 (no limit)
$config['max_recipients'] = 0;
// Maximum number of recipients per message exluding Bcc header.
// This is a soft limit, which means we only display a warning to the user.
// Default: 5
$config['max_disclosed_recipients'] = 5;
// Maximum allowed number of members of an address group. Default: 0 (no limit)
// If 'max_recipients' is set this value should be less or equal
$config['max_group_members'] = 0;
// Name your service. This is displayed on the login screen and in the window title
$config['product_name'] = 'Roundcube Webmail';
// Add this user-agent to message headers when sending
$config['useragent'] = 'Roundcube Webmail/'.RCMAIL_VERSION;
// try to load host-specific configuration
// see https://github.com/roundcube/roundcubemail/wiki/Configuration:-Multi-Domain-Setup
// for more details
$config['include_host_config'] = false;
// path to a text file which will be added to each sent message
// paths are relative to the Roundcube root folder
$config['generic_message_footer'] = '';
// path to a text file which will be added to each sent HTML message
// paths are relative to the Roundcube root folder
$config['generic_message_footer_html'] = '';
// add a received header to outgoing mails containing the creators IP and hostname
$config['http_received_header'] = false;
// Whether or not to encrypt the IP address and the host name
// these could, in some circles, be considered as sensitive information;
// however, for the administrator, these could be invaluable help
// when tracking down issues.
$config['http_received_header_encrypt'] = false;
// number of chars allowed for line when wrapping text.
// text wrapping is done when composing/sending messages
$config['line_length'] = 72;
// send plaintext messages as format=flowed
$config['send_format_flowed'] = true;
// According to RFC2298, return receipt envelope sender address must be empty.
// If this option is true, Roundcube will use user's identity as envelope sender for MDN responses.
$config['mdn_use_from'] = false;
// Set identities access level:
// 0 - many identities with possibility to edit all params
// 1 - many identities with possibility to edit all params but not email address
// 2 - one identity with possibility to edit all params
// 3 - one identity with possibility to edit all params but not email address
// 4 - one identity with possibility to edit only signature
$config['identities_level'] = 0;
// Maximum size of uploaded image in kilobytes
// Images (in html signatures) are stored in database as data URIs
$config['identity_image_size'] = 64;
// Mimetypes supported by the browser.
// attachments of these types will open in a preview window
// either a comma-separated list or an array: 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,application/pdf'
$config['client_mimetypes'] = null; # null == default
// Path to a local mime magic database file for PHPs finfo extension.
// Set to null if the default path should be used.
$config['mime_magic'] = null;
// Absolute path to a local mime.types mapping table file.
// This is used to derive mime-types from the filename extension or vice versa.
// Such a file is usually part of the apache webserver. If you don't find a file named mime.types on your system,
// download it from http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
$config['mime_types'] = null;
// path to imagemagick identify binary (if not set we'll use Imagick or GD extensions)
$config['im_identify_path'] = null;
// path to imagemagick convert binary (if not set we'll use Imagick or GD extensions)
$config['im_convert_path'] = null;
// Size of thumbnails from image attachments displayed below the message content.
// Note: whether images are displayed at all depends on the 'inline_images' option.
// Set to 0 to display images in full size.
$config['image_thumbnail_size'] = 240;
// maximum size of uploaded contact photos in pixel
$config['contact_photo_size'] = 160;
// Enable DNS checking for e-mail address validation
$config['email_dns_check'] = false;
// Disables saving sent messages in Sent folder (like gmail) (Default: false)
// Note: useful when SMTP server stores sent mail in user mailbox
$config['no_save_sent_messages'] = false;
// Improve system security by using special URL with security token.
// This can be set to a number defining token length. Default: 16.
// Warning: This requires http server configuration. Sample:
// RewriteRule ^/roundcubemail/[a-zA-Z0-9]{16}/(.*) /roundcubemail/$1 [PT]
// Alias /roundcubemail /var/www/roundcubemail/
// Note: Use assets_path to not prevent the browser from caching assets
$config['use_secure_urls'] = false;
// Allows to define separate server/path for image/js/css files
// Warning: If the domain is different cross-domain access to some
// resources need to be allowed
// Sample:
// <FilesMatch ".(eot|ttf|woff)">
// Header set Access-Control-Allow-Origin "*"
// </FilesMatch>
$config['assets_path'] = '';
// While assets_path is for the browser, assets_dir informs
// PHP code about the location of asset files in filesystem
$config['assets_dir'] = '';
// ----------------------------------
// PLUGINS
// ----------------------------------
// List of active plugins (in plugins/ directory)
$config['plugins'] = array();
// ----------------------------------
// USER INTERFACE
// ----------------------------------
// default messages sort column. Use empty value for default server's sorting,
// or 'arrival', 'date', 'subject', 'from', 'to', 'fromto', 'size', 'cc'
$config['message_sort_col'] = '';
// default messages sort order
$config['message_sort_order'] = 'DESC';
// These cols are shown in the message list. Available cols are:
// subject, from, to, fromto, cc, replyto, date, size, status, flag, attachment, priority
$config['list_cols'] = array('subject', 'status', 'fromto', 'date', 'size', 'flag', 'attachment');
// the default locale setting (leave empty for auto-detection)
// RFC1766 formatted language name like en_US, de_DE, de_CH, fr_FR, pt_BR
$config['language'] = null;
// use this format for date display (date or strftime format)
$config['date_format'] = 'Y-m-d';
// give this choice of date formats to the user to select from
// Note: do not use ambiguous formats like m/d/Y
$config['date_formats'] = array('Y-m-d', 'Y/m/d', 'Y.m.d', 'd-m-Y', 'd/m/Y', 'd.m.Y', 'j.n.Y');
// use this format for time display (date or strftime format)
$config['time_format'] = 'H:i';
// give this choice of time formats to the user to select from
$config['time_formats'] = array('G:i', 'H:i', 'g:i a', 'h:i A');
// use this format for short date display (derived from date_format and time_format)
$config['date_short'] = 'D H:i';
// use this format for detailed date/time formatting (derived from date_format and time_format)
$config['date_long'] = 'Y-m-d H:i';
// store draft message is this mailbox
// leave blank if draft messages should not be stored
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
$config['drafts_mbox'] = 'Drafts';
// store spam messages in this mailbox
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
$config['junk_mbox'] = 'Junk';
// store sent message is this mailbox
// leave blank if sent messages should not be stored
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
$config['sent_mbox'] = 'Sent';
// move messages to this folder when deleting them
// leave blank if they should be deleted directly
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
$config['trash_mbox'] = 'Trash';
// automatically create the above listed default folders on user login
$config['create_default_folders'] = false;
// protect the default folders from renames, deletes, and subscription changes
$config['protect_default_folders'] = true;
// Disable localization of the default folder names listed above
$config['show_real_foldernames'] = false;
// if in your system 0 quota means no limit set this option to true
$config['quota_zero_as_unlimited'] = false;
// Make use of the built-in spell checker. It is based on GoogieSpell.
$config['enable_spellcheck'] = true;
// Enables spellchecker exceptions dictionary.
// Setting it to 'shared' will make the dictionary shared by all users.
$config['spellcheck_dictionary'] = false;
// Set the spell checking engine. Possible values:
// - 'googie' - the default (also used for connecting to Nox Spell Server, see 'spellcheck_uri' setting)
// - 'pspell' - requires the PHP Pspell module and aspell installed
// - 'enchant' - requires the PHP Enchant module
// - 'atd' - install your own After the Deadline server or check with the people at http://www.afterthedeadline.com before using their API
// Since Google shut down their public spell checking service, the default settings
// connect to http://spell.roundcube.net which is a hosted service provided by Roundcube.
// You can connect to any other googie-compliant service by setting 'spellcheck_uri' accordingly.
$config['spellcheck_engine'] = 'googie';
// For locally installed Nox Spell Server or After the Deadline services,
// please specify the URI to call it.
// Get Nox Spell Server from http://orangoo.com/labs/?page_id=72 or
// the After the Deadline package from http://www.afterthedeadline.com.
// Leave empty to use the public API of service.afterthedeadline.com
$config['spellcheck_uri'] = '';
// These languages can be selected for spell checking.
// Configure as a PHP style hash array: array('en'=>'English', 'de'=>'Deutsch');
// Leave empty for default set of available language.
$config['spellcheck_languages'] = NULL;
// Makes that words with all letters capitalized will be ignored (e.g. GOOGLE)
$config['spellcheck_ignore_caps'] = false;
// Makes that words with numbers will be ignored (e.g. g00gle)
$config['spellcheck_ignore_nums'] = false;
// Makes that words with symbols will be ignored (e.g. g@@gle)
$config['spellcheck_ignore_syms'] = false;
// Number of lines at the end of a message considered to contain the signature.
// Increase this value if signatures are not properly detected and colored
$config['sig_max_lines'] = 15;
// don't let users set pagesize to more than this value if set
$config['max_pagesize'] = 200;
// Minimal value of user's 'refresh_interval' setting (in seconds)
$config['min_refresh_interval'] = 60;
// Enables files upload indicator. Requires APC installed and enabled apc.rfc1867 option.
// By default refresh time is set to 1 second. You can set this value to true
// or any integer value indicating number of seconds.
$config['upload_progress'] = false;
// Specifies for how many seconds the Undo button will be available
// after object delete action. Currently used with supporting address book sources.
// Setting it to 0, disables the feature.
$config['undo_timeout'] = 0;
// A static list of canned responses which are immutable for the user
$config['compose_responses_static'] = array(
// array('name' => 'Canned Response 1', 'text' => 'Static Response One'),
// array('name' => 'Canned Response 2', 'text' => 'Static Response Two'),
);
// ----------------------------------
// ADDRESSBOOK SETTINGS
// ----------------------------------
// This indicates which type of address book to use. Possible choises:
// 'sql' (default), 'ldap' and ''.
// If set to 'ldap' then it will look at using the first writable LDAP
// address book as the primary address book and it will not display the
// SQL address book in the 'Address Book' view.
// If set to '' then no address book will be displayed or only the
// addressbook which is created by a plugin (like CardDAV).
$config['address_book_type'] = 'sql';
// In order to enable public ldap search, configure an array like the Verisign
// example further below. if you would like to test, simply uncomment the example.
// Array key must contain only safe characters, ie. a-zA-Z0-9_
$config['ldap_public'] = array();
// If you are going to use LDAP for individual address books, you will need to
// set 'user_specific' to true and use the variables to generate the appropriate DNs to access it.
//
// The recommended directory structure for LDAP is to store all the address book entries
// under the users main entry, e.g.:
//
// o=root
// ou=people
// uid=user@domain
// mail=contact@contactdomain
//
// So the base_dn would be uid=%fu,ou=people,o=root
// The bind_dn would be the same as based_dn or some super user login.
/*
* example config for Verisign directory
*
$config['ldap_public']['Verisign'] = array(
'name' => 'Verisign.com',
// Replacement variables supported in host names:
// %h - user's IMAP hostname
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
// %z - IMAP domain (IMAP hostname without the first part)
// For example %n = mail.domain.tld, %t = domain.tld
'hosts' => array('directory.verisign.com'),
'port' => 389,
'use_tls' => false,
'ldap_version' => 3, // using LDAPv3
'network_timeout' => 10, // The timeout (in seconds) for connect + bind arrempts. This is only supported in PHP >= 5.3.0 with OpenLDAP 2.x
'user_specific' => false, // If true the base_dn, bind_dn and bind_pass default to the user's IMAP login.
// When 'user_specific' is enabled following variables can be used in base_dn/bind_dn config:
// %fu - The full username provided, assumes the username is an email
// address, uses the username_domain value if not an email address.
// %u - The username prior to the '@'.
// %d - The domain name after the '@'.
// %dc - The domain name hierarchal string e.g. "dc=test,dc=domain,dc=com"
// %dn - DN found by ldap search when search_filter/search_base_dn are used
'base_dn' => '',
'bind_dn' => '',
'bind_pass' => '',
// It's possible to bind for an individual address book
// The login name is used to search for the DN to bind with
'search_base_dn' => '',
'search_filter' => '', // e.g. '(&(objectClass=posixAccount)(uid=%u))'
// DN and password to bind as before searching for bind DN, if anonymous search is not allowed
'search_bind_dn' => '',
'search_bind_pw' => '',
// Base DN and filter used for resolving the user's domain root DN which feeds the %dc variables
// Leave empty to skip this lookup and derive the root DN from the username domain
'domain_base_dn' => '',
'domain_filter' => '',
// Optional map of replacement strings => attributes used when binding for an individual address book
'search_bind_attrib' => array(), // e.g. array('%udc' => 'ou')
// Default for %dn variable if search doesn't return DN value
'search_dn_default' => '',
// Optional authentication identifier to be used as SASL authorization proxy
// bind_dn need to be empty
'auth_cid' => '',
// SASL authentication method (for proxy auth), e.g. DIGEST-MD5
'auth_method' => '',
// Indicates if the addressbook shall be hidden from the list.
// With this option enabled you can still search/view contacts.
'hidden' => false,
// Indicates if the addressbook shall not list contacts but only allows searching.
'searchonly' => false,
// Indicates if we can write to the LDAP directory or not.
// If writable is true then these fields need to be populated:
// LDAP_Object_Classes, required_fields, LDAP_rdn
'writable' => false,
// To create a new contact these are the object classes to specify
// (or any other classes you wish to use).
'LDAP_Object_Classes' => array('top', 'inetOrgPerson'),
// The RDN field that is used for new entries, this field needs
// to be one of the search_fields, the base of base_dn is appended
// to the RDN to insert into the LDAP directory.
'LDAP_rdn' => 'cn',
// The required fields needed to build a new contact as required by
// the object classes (can include additional fields not required by the object classes).
'required_fields' => array('cn', 'sn', 'mail'),
'search_fields' => array('mail', 'cn'), // fields to search in
// mapping of contact fields to directory attributes
// 1. for every attribute one can specify the number of values (limit) allowed.
// default is 1, a wildcard * means unlimited
// 2. another possible parameter is separator character for composite fields
// 3. it's possible to define field format for write operations, e.g. for date fields
// example: 'birthday:date[YmdHis\\Z]'
'fieldmap' => array(
// Roundcube => LDAP:limit
'name' => 'cn',
'surname' => 'sn',
'firstname' => 'givenName',
'jobtitle' => 'title',
'email' => 'mail:*',
'phone:home' => 'homePhone',
'phone:work' => 'telephoneNumber',
'phone:mobile' => 'mobile',
'phone:pager' => 'pager',
'phone:workfax' => 'facsimileTelephoneNumber',
'street' => 'street',
'zipcode' => 'postalCode',
'region' => 'st',
'locality' => 'l',
// if you country is a complex object, you need to configure 'sub_fields' below
'country' => 'c',
'organization' => 'o',
'department' => 'ou',
'jobtitle' => 'title',
'notes' => 'description',
'photo' => 'jpegPhoto',
// these currently don't work:
// 'manager' => 'manager',
// 'assistant' => 'secretary',
),
// Map of contact sub-objects (attribute name => objectClass(es)), e.g. 'c' => 'country'
'sub_fields' => array(),
// Generate values for the following LDAP attributes automatically when creating a new record
'autovalues' => array(
// 'uid' => 'md5(microtime())', // You may specify PHP code snippets which are then eval'ed
// 'mail' => '{givenname}.{sn}@mydomain.com', // or composite strings with placeholders for existing attributes
),
'sort' => 'cn', // The field to sort the listing by.
'scope' => 'sub', // search mode: sub|base|list
'filter' => '(objectClass=inetOrgPerson)', // used for basic listing (if not empty) and will be &'d with search queries. example: status=act
'fuzzy_search' => true, // server allows wildcard search
'vlv' => false, // Enable Virtual List View to more efficiently fetch paginated data (if server supports it)
'vlv_search' => false, // Use Virtual List View functions for autocompletion searches (if server supports it)
'numsub_filter' => '(objectClass=organizationalUnit)', // with VLV, we also use numSubOrdinates to query the total number of records. Set this filter to get all numSubOrdinates attributes for counting
'config_root_dn' => 'cn=config', // Root DN to search config entries (e.g. vlv indexes)
'sizelimit' => '0', // Enables you to limit the count of entries fetched. Setting this to 0 means no limit.
'timelimit' => '0', // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit.
'referrals' => false, // Sets the LDAP_OPT_REFERRALS option. Mostly used in multi-domain Active Directory setups
'dereference' => 0, // Sets the LDAP_OPT_DEREF option. One of: LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, LDAP_DEREF_FINDING, LDAP_DEREF_ALWAYS
// Used where addressbook contains aliases to objects elsewhere in the LDAP tree.
// definition for contact groups (uncomment if no groups are supported)
// for the groups base_dn, the user replacements %fu, %u, %d and %dc work as for base_dn (see above)
// if the groups base_dn is empty, the contact base_dn is used for the groups as well
// -> in this case, assure that groups and contacts are separated due to the concernig filters!
'groups' => array(
'base_dn' => '',
'scope' => 'sub', // Search mode: sub|base|list
'filter' => '(objectClass=groupOfNames)',
'object_classes' => array('top', 'groupOfNames'), // Object classes to be assigned to new groups
'member_attr' => 'member', // Name of the default member attribute, e.g. uniqueMember
'name_attr' => 'cn', // Attribute to be used as group name
'email_attr' => 'mail', // Group email address attribute (e.g. for mailing lists)
'member_filter' => '(objectclass=*)', // Optional filter to use when querying for group members
'vlv' => false, // Use VLV controls to list groups
'class_member_attr' => array( // Mapping of group object class to member attribute used in these objects
'groupofnames' => 'member',
'groupofuniquenames' => 'uniquemember'
),
),
// this configuration replaces the regular groups listing in the directory tree with
// a hard-coded list of groups, each listing entries with the configured base DN and filter.
// if the 'groups' option from above is set, it'll be shown as the first entry with the name 'Groups'
'group_filters' => array(
'departments' => array(
'name' => 'Company Departments',
'scope' => 'list',
'base_dn' => 'ou=Groups,dc=mydomain,dc=com',
'filter' => '(|(objectclass=groupofuniquenames)(objectclass=groupofurls))',
'name_attr' => 'cn',
),
'customers' => array(
'name' => 'Customers',
'scope' => 'sub',
'base_dn' => 'ou=Customers,dc=mydomain,dc=com',
'filter' => '(objectClass=inetOrgPerson)',
'name_attr' => 'sn',
),
),
);
*/
// An ordered array of the ids of the addressbooks that should be searched
// when populating address autocomplete fields server-side. ex: array('sql','Verisign');
$config['autocomplete_addressbooks'] = array('sql');
// The minimum number of characters required to be typed in an autocomplete field
// before address books will be searched. Most useful for LDAP directories that
// may need to do lengthy results building given overly-broad searches
$config['autocomplete_min_length'] = 1;
// Number of parallel autocomplete requests.
// If there's more than one address book, n parallel (async) requests will be created,
// where each request will search in one address book. By default (0), all address
// books are searched in one request.
$config['autocomplete_threads'] = 0;
// Max. numer of entries in autocomplete popup. Default: 15.
$config['autocomplete_max'] = 15;
// show address fields in this order
// available placeholders: {street}, {locality}, {zipcode}, {country}, {region}
$config['address_template'] = '{street}<br/>{locality} {zipcode}<br/>{country} {region}';
// Matching mode for addressbook search (including autocompletion)
// 0 - partial (*abc*), default
// 1 - strict (abc)
// 2 - prefix (abc*)
// Note: For LDAP sources fuzzy_search must be enabled to use 'partial' or 'prefix' mode
$config['addressbook_search_mode'] = 0;
// List of fields used on contacts list and for autocompletion searches
// Warning: These are field names not LDAP attributes (see 'fieldmap' setting)!
$config['contactlist_fields'] = array('name', 'firstname', 'surname', 'email');
// Template of contact entry on the autocompletion list.
// You can use contact fields as: name, email, organization, department, etc.
// See program/steps/addressbook/func.inc for a list
$config['contact_search_name'] = '{name} <{email}>';
// ----------------------------------
// USER PREFERENCES
// ----------------------------------
// Use this charset as fallback for message decoding
$config['default_charset'] = 'ISO-8859-1';
// skin name: folder from skins/
$config['skin'] = 'larry';
// Enables using standard browser windows (that can be handled as tabs)
// instead of popup windows
$config['standard_windows'] = false;
// show up to X items in messages list view
$config['mail_pagesize'] = 50;
// show up to X items in contacts list view
$config['addressbook_pagesize'] = 50;
// sort contacts by this col (preferably either one of name, firstname, surname)
$config['addressbook_sort_col'] = 'surname';
// The way how contact names are displayed in the list.
// 0: prefix firstname middlename surname suffix (only if display name is not set)
// 1: firstname middlename surname
// 2: surname firstname middlename
// 3: surname, firstname middlename
$config['addressbook_name_listing'] = 0;
// use this timezone to display date/time
// valid timezone identifiers are listed here: php.net/manual/en/timezones.php
// 'auto' will use the browser's timezone settings
$config['timezone'] = 'auto';
// prefer displaying HTML messages
$config['prefer_html'] = true;
// display remote resources (inline images, styles)
// 0 - Never, always ask
// 1 - Ask if sender is not in address book
// 2 - Always allow
$config['show_images'] = 0;
// open messages in new window
$config['message_extwin'] = false;
// open message compose form in new window
$config['compose_extwin'] = false;
// compose html formatted messages by default
// 0 - never,
// 1 - always,
// 2 - on reply to HTML message,
// 3 - on forward or reply to HTML message
// 4 - always, except when replying to plain text message
$config['htmleditor'] = 0;
// save copies of compose messages in the browser's local storage
// for recovery in case of browser crashes and session timeout.
$config['compose_save_localstorage'] = true;
// show pretty dates as standard
$config['prettydate'] = true;
// save compose message every 300 seconds (5min)
$config['draft_autosave'] = 300;
// Interface layout. Default: 'widescreen'.
// 'widescreen' - three columns
// 'desktop' - two columns, preview on bottom
// 'list' - two columns, no preview
$config['layout'] = 'widescreen';
// Mark as read when viewing a message (delay in seconds)
// Set to -1 if messages should not be marked as read
$config['mail_read_time'] = 0;
// Clear Trash on logout
$config['logout_purge'] = false;
// Compact INBOX on logout
$config['logout_expunge'] = false;
// Display attached images below the message body
$config['inline_images'] = true;
// Encoding of long/non-ascii attachment names:
// 0 - Full RFC 2231 compatible
// 1 - RFC 2047 for 'name' and RFC 2231 for 'filename' parameter (Thunderbird's default)
// 2 - Full 2047 compatible
$config['mime_param_folding'] = 1;
// Set true if deleted messages should not be displayed
// This will make the application run slower
$config['skip_deleted'] = false;
// Set true to Mark deleted messages as read as well as deleted
// False means that a message's read status is not affected by marking it as deleted
$config['read_when_deleted'] = true;
// Set to true to never delete messages immediately
// Use 'Purge' to remove messages marked as deleted
$config['flag_for_deletion'] = false;
// Default interval for auto-refresh requests (in seconds)
// These are requests for system state updates e.g. checking for new messages, etc.
// Setting it to 0 disables the feature.
$config['refresh_interval'] = 60;
// If true all folders will be checked for recent messages
$config['check_all_folders'] = false;
// If true, after message delete/move, the next message will be displayed
$config['display_next'] = true;
// Default messages listing mode. One of 'threads' or 'list'.
$config['default_list_mode'] = 'list';
// 0 - Do not expand threads
// 1 - Expand all threads automatically
// 2 - Expand only threads with unread messages
$config['autoexpand_threads'] = 0;
// When replying:
// -1 - don't cite the original message
// 0 - place cursor below the original message
// 1 - place cursor above original message (top posting)
// 2 - place cursor above original message (top posting), but do not indent the quote
$config['reply_mode'] = 0;
// When replying strip original signature from message
$config['strip_existing_sig'] = true;
// Show signature:
// 0 - Never
// 1 - Always
// 2 - New messages only
// 3 - Forwards and Replies only
$config['show_sig'] = 1;
// By default the signature is placed depending on cursor position (reply_mode).
// Sometimes it might be convenient to start the reply on top but keep
// the signature below the quoted text (sig_below = true).
$config['sig_below'] = false;
// Enables adding of standard separator to the signature
$config['sig_separator'] = true;
// Use MIME encoding (quoted-printable) for 8bit characters in message body
$config['force_7bit'] = false;
// Defaults of the search field configuration.
// The array can contain a per-folder list of header fields which should be considered when searching
// The entry with key '*' stands for all folders which do not have a specific list set.
// Please note that folder names should to be in sync with $config['*_mbox'] options
$config['search_mods'] = null; // Example: array('*' => array('subject'=>1, 'from'=>1), 'Sent' => array('subject'=>1, 'to'=>1));
// Defaults of the addressbook search field configuration.
$config['addressbook_search_mods'] = null; // Example: array('name'=>1, 'firstname'=>1, 'surname'=>1, 'email'=>1, '*'=>1);
// 'Delete always'
// This setting reflects if mail should be always deleted
// when moving to Trash fails. This is necessary in some setups
// when user is over quota and Trash is included in the quota.
$config['delete_always'] = false;
// Directly delete messages in Junk instead of moving to Trash
$config['delete_junk'] = false;
// Behavior if a received message requests a message delivery notification (read receipt)
// 0 = ask the user, 1 = send automatically, 2 = ignore (never send or ask)
// 3 = send automatically if sender is in addressbook, otherwise ask the user
// 4 = send automatically if sender is in addressbook, otherwise ignore
$config['mdn_requests'] = 0;
// Return receipt checkbox default state
$config['mdn_default'] = 0;
// Delivery Status Notification checkbox default state
// Note: This can be used only if smtp_server is non-empty
$config['dsn_default'] = 0;
// Place replies in the folder of the message being replied to
$config['reply_same_folder'] = false;
// Sets default mode of Forward feature to "forward as attachment"
$config['forward_attachment'] = false;
// Defines address book (internal index) to which new contacts will be added
// By default it is the first writeable addressbook.
// Note: Use '0' for built-in address book.
$config['default_addressbook'] = null;
// Enables spell checking before sending a message.
$config['spellcheck_before_send'] = false;
// Skip alternative email addresses in autocompletion (show one address per contact)
$config['autocomplete_single'] = false;
// Default font for composed HTML message.
// Supported values: Andale Mono, Arial, Arial Black, Book Antiqua, Courier New,
// Georgia, Helvetica, Impact, Tahoma, Terminal, Times New Roman, Trebuchet MS, Verdana
$config['default_font'] = 'Verdana';
// Default font size for composed HTML message.
// Supported sizes: 8pt, 10pt, 12pt, 14pt, 18pt, 24pt, 36pt
$config['default_font_size'] = '10pt';
// Enables display of email address with name instead of a name (and address in title)
$config['message_show_email'] = false;
// Default behavior of Reply-All button:
// 0 - Reply-All always
// 1 - Reply-List if mailing list is detected
$config['reply_all_mode'] = 0;
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 2708db559..f823fab51 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -1,2557 +1,2551 @@
<?php
/**
+-----------------------------------------------------------------------+
| program/include/rcmail.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2008-2014, The Roundcube Dev Team |
| Copyright (C) 2011-2014, 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: |
| Application class providing core functions and holding |
| instances of all 'global' objects like db- and imap-connections |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Application class of Roundcube Webmail
* implemented as singleton
*
* @package Webmail
*/
class rcmail extends rcube
{
/**
* Main tasks.
*
* @var array
*/
static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy');
/**
* Current task.
*
* @var string
*/
public $task;
/**
* Current action.
*
* @var string
*/
public $action = '';
public $comm_path = './';
public $filename = '';
private $address_books = array();
private $action_map = array();
const ERROR_STORAGE = -2;
const ERROR_INVALID_REQUEST = 1;
const ERROR_INVALID_HOST = 2;
const ERROR_COOKIES_DISABLED = 3;
const ERROR_RATE_LIMIT = 4;
/**
* This implements the 'singleton' design pattern
*
* @param integer $mode Ignored rcube::get_instance() argument
* @param string $env Environment name to run (e.g. live, dev, test)
*
* @return rcmail The one and only instance
*/
static function get_instance($mode = 0, $env = '')
{
if (!self::$instance || !is_a(self::$instance, 'rcmail')) {
self::$instance = new rcmail($env);
// init AFTER object was linked with self::$instance
self::$instance->startup();
}
return self::$instance;
}
/**
* Initial startup function
* to register session, create database and imap connections
*/
protected function startup()
{
$this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS);
// set filename if not index.php
if (($basename = basename($_SERVER['SCRIPT_FILENAME'])) && $basename != 'index.php') {
$this->filename = $basename;
}
// load all configured plugins
$plugins = (array) $this->config->get('plugins', array());
$required_plugins = array('filesystem_attachments', 'jqueryui');
$this->plugins->load_plugins($plugins, $required_plugins);
// Remember default skin, before it's replaced by user prefs
$this->default_skin = $this->config->get('skin');
// start session
$this->session_init();
// create user object
$this->set_user(new rcube_user($_SESSION['user_id']));
// set task and action properties
$this->set_task(rcube_utils::get_input_value('_task', rcube_utils::INPUT_GPC));
$this->action = asciiwords(rcube_utils::get_input_value('_action', rcube_utils::INPUT_GPC));
// reset some session parameters when changing task
if ($this->task != 'utils') {
// we reset list page when switching to another task
// but only to the main task interface - empty action (#1489076, #1490116)
// this will prevent from unintentional page reset on cross-task requests
if ($this->session && $_SESSION['task'] != $this->task && empty($this->action)) {
$this->session->remove('page');
// set current task to session
$_SESSION['task'] = $this->task;
}
}
// init output class (not in CLI mode)
if (!empty($_REQUEST['_remote'])) {
$GLOBALS['OUTPUT'] = $this->json_init();
}
else if ($_SERVER['REMOTE_ADDR']) {
$GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed']));
}
// run init method on all the plugins
$this->plugins->init($this, $this->task);
}
/**
* Setter for application task
*
* @param string $task Task to set
*/
public function set_task($task)
{
if (php_sapi_name() == 'cli') {
$task = 'cli';
}
else if (!$this->user || !$this->user->ID) {
$task = 'login';
}
else {
$task = asciiwords($task, true) ?: 'mail';
}
$this->task = $task;
$this->comm_path = $this->url(array('task' => $this->task));
if (!empty($_REQUEST['_framed'])) {
$this->comm_path .= '&_framed=1';
}
if ($this->output) {
$this->output->set_env('task', $this->task);
$this->output->set_env('comm_path', $this->comm_path);
}
}
/**
* Setter for system user object
*
* @param rcube_user $user Current user instance
*/
public function set_user($user)
{
parent::set_user($user);
$lang = $this->language_prop($this->config->get('language', $_SESSION['language']));
$_SESSION['language'] = $this->user->language = $lang;
// set localization
setlocale(LC_ALL, $lang . '.utf8', $lang . '.UTF-8', 'en_US.utf8', 'en_US.UTF-8');
// Workaround for http://bugs.php.net/bug.php?id=18556
// Also strtoupper/strtolower and other methods are locale-aware
// for these locales it is problematic (#1490519)
if (in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) {
setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8', 'C');
}
}
/**
* Return instance of the internal address book class
*
* @param string $id Address book identifier (-1 for default addressbook)
* @param boolean $writeable True if the address book needs to be writeable
*
* @return rcube_contacts Address book object
*/
public function get_address_book($id, $writeable = false)
{
$contacts = null;
$ldap_config = (array)$this->config->get('ldap_public');
// 'sql' is the alias for '0' used by autocomplete
if ($id == 'sql')
$id = '0';
else if ($id == -1) {
$id = $this->config->get('default_addressbook');
$default = true;
}
// use existing instance
if (isset($this->address_books[$id]) && ($this->address_books[$id] instanceof rcube_addressbook)) {
$contacts = $this->address_books[$id];
}
else if ($id && $ldap_config[$id]) {
$domain = $this->config->mail_domain($_SESSION['storage_host']);
$contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $domain);
}
else if ($id === '0') {
$contacts = new rcube_contacts($this->db, $this->get_user_id());
}
else {
$plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable));
// plugin returned instance of a rcube_addressbook
if ($plugin['instance'] instanceof rcube_addressbook) {
$contacts = $plugin['instance'];
}
}
// when user requested default writeable addressbook
// we need to check if default is writeable, if not we
// will return first writeable book (if any exist)
if ($contacts && $default && $contacts->readonly && $writeable) {
$contacts = null;
}
// Get first addressbook from the list if configured default doesn't exist
// This can happen when user deleted the addressbook (e.g. Kolab folder)
if (!$contacts && (!$id || $default)) {
$source = reset($this->get_address_sources($writeable, !$default));
if (!empty($source)) {
$contacts = $this->get_address_book($source['id']);
if ($contacts) {
$id = $source['id'];
}
}
}
if (!$contacts) {
// there's no default, just return
if ($default) {
return null;
}
self::raise_error(array(
'code' => 700,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Addressbook source ($id) not found!"
),
true, true);
}
// add to the 'books' array for shutdown function
$this->address_books[$id] = $contacts;
if ($writeable && $contacts->readonly) {
return null;
}
// set configured sort order
if ($sort_col = $this->config->get('addressbook_sort_col')) {
$contacts->set_sort_order($sort_col);
}
return $contacts;
}
/**
* Return identifier of the address book object
*
* @param rcube_addressbook $object Addressbook source object
*
* @return string Source identifier
*/
public function get_address_book_id($object)
{
foreach ($this->address_books as $index => $book) {
if ($book === $object) {
return $index;
}
}
}
/**
* Return address books list
*
* @param boolean $writeable True if the address book needs to be writeable
* @param boolean $skip_hidden True if the address book needs to be not hidden
*
* @return array Address books array
*/
public function get_address_sources($writeable = false, $skip_hidden = false)
{
$abook_type = (string) $this->config->get('address_book_type');
$ldap_config = (array) $this->config->get('ldap_public');
$autocomplete = (array) $this->config->get('autocomplete_addressbooks');
$list = array();
// We are using the DB address book or a plugin address book
if (!empty($abook_type) && strtolower($abook_type) != 'ldap') {
if (!isset($this->address_books['0'])) {
$this->address_books['0'] = new rcube_contacts($this->db, $this->get_user_id());
}
$list['0'] = array(
'id' => '0',
'name' => $this->gettext('personaladrbook'),
'groups' => $this->address_books['0']->groups,
'readonly' => $this->address_books['0']->readonly,
'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'),
'autocomplete' => in_array('sql', $autocomplete),
);
}
if (!empty($ldap_config)) {
foreach ($ldap_config as $id => $prop) {
// handle misconfiguration
if (empty($prop) || !is_array($prop)) {
continue;
}
$list[$id] = array(
'id' => $id,
'name' => html::quote($prop['name']),
'groups' => !empty($prop['groups']) || !empty($prop['group_filters']),
'readonly' => !$prop['writable'],
'hidden' => $prop['hidden'],
'autocomplete' => in_array($id, $autocomplete)
);
}
}
$plugin = $this->plugins->exec_hook('addressbooks_list', array('sources' => $list));
$list = $plugin['sources'];
foreach ($list as $idx => $item) {
// register source for shutdown function
if (!is_object($this->address_books[$item['id']])) {
$this->address_books[$item['id']] = $item;
}
// remove from list if not writeable as requested
if ($writeable && $item['readonly']) {
unset($list[$idx]);
}
// remove from list if hidden as requested
else if ($skip_hidden && $item['hidden']) {
unset($list[$idx]);
}
}
return $list;
}
/**
* Getter for compose responses.
* These are stored in local config and user preferences.
*
* @param boolean $sorted True to sort the list alphabetically
* @param boolean $user_only True if only this user's responses shall be listed
*
* @return array List of the current user's stored responses
*/
public function get_compose_responses($sorted = false, $user_only = false)
{
$responses = array();
if (!$user_only) {
foreach ($this->config->get('compose_responses_static', array()) as $response) {
if (empty($response['key'])) {
$response['key'] = substr(md5($response['name']), 0, 16);
}
$response['static'] = true;
$response['class'] = 'readonly';
$k = $sorted ? '0000-' . mb_strtolower($response['name']) : $response['key'];
$responses[$k] = $response;
}
}
foreach ($this->config->get('compose_responses', array()) as $response) {
if (empty($response['key'])) {
$response['key'] = substr(md5($response['name']), 0, 16);
}
$k = $sorted ? mb_strtolower($response['name']) : $response['key'];
$responses[$k] = $response;
}
// sort list by name
if ($sorted) {
ksort($responses, SORT_LOCALE_STRING);
}
$responses = array_values($responses);
$hook = $this->plugins->exec_hook('get_compose_responses', array(
'list' => $responses,
'sorted' => $sorted,
'user_only' => $user_only,
));
return $hook['list'];
}
/**
* Init output object for GUI and add common scripts.
* This will instantiate a rcmail_output_html object and set
* environment vars according to the current session and configuration
*
* @param boolean $framed True if this request is loaded in a (i)frame
*
* @return rcube_output Reference to HTML output object
*/
public function load_gui($framed = false)
{
// init output page
if (!($this->output instanceof rcmail_output_html)) {
$this->output = new rcmail_output_html($this->task, $framed);
}
// set refresh interval
$this->output->set_env('refresh_interval', $this->config->get('refresh_interval', 0));
$this->output->set_env('session_lifetime', $this->config->get('session_lifetime', 0) * 60);
if ($framed) {
$this->comm_path .= '&_framed=1';
$this->output->set_env('framed', true);
}
$this->output->set_env('task', $this->task);
$this->output->set_env('action', $this->action);
$this->output->set_env('comm_path', $this->comm_path);
$this->output->set_charset(RCUBE_CHARSET);
if ($this->user && $this->user->ID) {
$this->output->set_env('user_id', $this->user->get_hash());
}
// set compose mode for all tasks (message compose step can be triggered from everywhere)
$this->output->set_env('compose_extwin', $this->config->get('compose_extwin',false));
// add some basic labels to client
$this->output->add_label('loading', 'servererror', 'connerror', 'requesttimedout',
'refreshing', 'windowopenerror', 'uploadingmany', 'uploading', 'close', 'save', 'cancel',
'alerttitle', 'confirmationtitle', 'delete', 'continue', 'ok');
return $this->output;
}
/**
* Create an output object for JSON responses
*
* @return rcube_output Reference to JSON output object
*/
public function json_init()
{
if (!($this->output instanceof rcmail_output_json)) {
$this->output = new rcmail_output_json($this->task);
}
return $this->output;
}
/**
* Create session object and start the session.
*/
public function session_init()
{
parent::session_init();
// set initial session vars
if (!$_SESSION['user_id']) {
$_SESSION['temp'] = true;
}
}
/**
* Perform login to the mail server and to the webmail service.
* This will also create a new user entry if auto_create_user is configured.
*
* @param string $username Mail storage (IMAP) user name
* @param string $password Mail storage (IMAP) password
* @param string $host Mail storage (IMAP) host
* @param bool $cookiecheck Enables cookie check
*
* @return boolean True on success, False on failure
*/
function login($username, $password, $host = null, $cookiecheck = false)
{
$this->login_error = null;
if (empty($username)) {
return false;
}
if ($cookiecheck && empty($_COOKIE)) {
$this->login_error = self::ERROR_COOKIES_DISABLED;
return false;
}
$username_filter = $this->config->get('login_username_filter');
$username_maxlen = $this->config->get('login_username_maxlen', 1024);
$password_maxlen = $this->config->get('login_password_maxlen', 1024);
$default_host = $this->config->get('default_host');
$default_port = $this->config->get('default_port');
$username_domain = $this->config->get('username_domain');
$login_lc = $this->config->get('login_lc', 2);
// check input for security (#1490500)
if (($username_maxlen && strlen($username) > $username_maxlen)
|| ($username_filter && !preg_match($username_filter, $username))
|| ($password_maxlen && strlen($password) > $password_maxlen)
) {
$this->login_error = self::ERROR_INVALID_REQUEST;
return false;
}
// host is validated in rcmail::autoselect_host(), so here
// we'll only handle unset host (if possible)
if (!$host && !empty($default_host)) {
if (is_array($default_host)) {
$key = key($default_host);
$host = is_numeric($key) ? $default_host[$key] : $key;
}
else {
$host = $default_host;
}
$host = rcube_utils::parse_host($host);
}
if (!$host) {
$this->login_error = self::ERROR_INVALID_HOST;
return false;
}
// parse $host URL
$a_host = parse_url($host);
if ($a_host['host']) {
$host = $a_host['host'];
$ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
if (!empty($a_host['port']))
$port = $a_host['port'];
else if ($ssl && $ssl != 'tls' && (!$default_port || $default_port == 143))
$port = 993;
}
if (!$port) {
$port = $default_port;
}
// Check if we need to add/force domain to username
if (!empty($username_domain)) {
$domain = is_array($username_domain) ? $username_domain[$host] : $username_domain;
if ($domain = rcube_utils::parse_host((string)$domain, $host)) {
$pos = strpos($username, '@');
// force configured domains
if ($pos !== false && $this->config->get('username_domain_forced')) {
$username = substr($username, 0, $pos) . '@' . $domain;
}
// just add domain if not specified
else if ($pos === false) {
$username .= '@' . $domain;
}
}
}
// Convert username to lowercase. If storage backend
// is case-insensitive we need to store always the same username (#1487113)
if ($login_lc) {
if ($login_lc == 2 || $login_lc === true) {
$username = mb_strtolower($username);
}
else if (strpos($username, '@')) {
// lowercase domain name
list($local, $domain) = explode('@', $username);
$username = $local . '@' . mb_strtolower($domain);
}
}
// try to resolve email address from virtuser table
if (strpos($username, '@') && ($virtuser = rcube_user::email2user($username))) {
$username = $virtuser;
}
// Here we need IDNA ASCII
// Only rcube_contacts class is using domain names in Unicode
$host = rcube_utils::idn_to_ascii($host);
if (strpos($username, '@')) {
$username = rcube_utils::idn_to_ascii($username);
}
// user already registered -> overwrite username
if ($user = rcube_user::query($username, $host)) {
$username = $user->data['username'];
// Brute-force prevention
if ($user->is_locked()) {
$this->login_error = self::ERROR_RATE_LIMIT;
return false;
}
}
$storage = $this->get_storage();
// try to log in
if (!$storage->connect($host, $username, $password, $port, $ssl)) {
if ($user) {
$user->failed_login();
}
// Wait a second to slow down brute-force attacks (#1490549)
sleep(1);
return false;
}
// user already registered -> update user's record
if (is_object($user)) {
// update last login timestamp
$user->touch();
}
// create new system user
else if ($this->config->get('auto_create_user')) {
if ($created = rcube_user::create($username, $host)) {
$user = $created;
}
else {
self::raise_error(array(
'code' => 620,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Failed to create a user record. Maybe aborted by a plugin?"
),
true, false);
}
}
else {
self::raise_error(array(
'code' => 621,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Access denied for new user $username. 'auto_create_user' is disabled"
),
true, false);
}
// login succeeded
if (is_object($user) && $user->ID) {
// Configure environment
$this->set_user($user);
$this->set_storage_prop();
// set session vars
$_SESSION['user_id'] = $user->ID;
$_SESSION['username'] = $user->data['username'];
$_SESSION['storage_host'] = $host;
$_SESSION['storage_port'] = $port;
$_SESSION['storage_ssl'] = $ssl;
$_SESSION['password'] = $this->encrypt($password);
$_SESSION['login_time'] = time();
$timezone = rcube_utils::get_input_value('_timezone', rcube_utils::INPUT_GPC);
if ($timezone && is_string($timezone) && $timezone != '_default_') {
$_SESSION['timezone'] = $timezone;
}
// fix some old settings according to namespace prefix
$this->fix_namespace_settings($user);
// set/create special folders
$this->set_special_folders();
// clear all mailboxes related cache(s)
$storage->clear_cache('mailboxes', true);
return true;
}
return false;
}
/**
* Returns error code of last login operation
*
* @return int Error code
*/
public function login_error()
{
if ($this->login_error) {
return $this->login_error;
}
if ($this->storage && $this->storage->get_error_code() < -1) {
return self::ERROR_STORAGE;
}
}
/**
* Auto-select IMAP host based on the posted login information
*
* @return string Selected IMAP host
*/
public function autoselect_host()
{
$default_host = $this->config->get('default_host');
$host = null;
if (is_array($default_host)) {
$post_host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST);
$post_user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST);
list(, $domain) = explode('@', $post_user);
// direct match in default_host array
if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) {
$host = $post_host;
}
// try to select host by mail domain
else if (!empty($domain)) {
foreach ($default_host as $storage_host => $mail_domains) {
if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) {
$host = $storage_host;
break;
}
else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) {
$host = is_numeric($storage_host) ? $mail_domains : $storage_host;
break;
}
}
}
// take the first entry if $host is still not set
if (empty($host)) {
$key = key($default_host);
$host = is_numeric($key) ? $default_host[$key] : $key;
}
}
else if (empty($default_host)) {
$host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST);
}
else {
$host = rcube_utils::parse_host($default_host);
}
return $host;
}
/**
* Destroy session data and remove cookie
*/
public function kill_session()
{
$this->plugins->exec_hook('session_destroy');
$this->session->kill();
$_SESSION = array('language' => $this->user->language, 'temp' => true);
$this->user->reset();
if ($this->config->get('skin') != $this->default_skin && method_exists($this->output, 'set_skin')) {
$this->output->set_skin($this->default_skin);
}
}
/**
* Do server side actions on logout
*/
public function logout_actions()
{
$storage = $this->get_storage();
$logout_expunge = $this->config->get('logout_expunge');
$logout_purge = $this->config->get('logout_purge');
$trash_mbox = $this->config->get('trash_mbox');
if ($logout_purge && !empty($trash_mbox)) {
$storage->clear_folder($trash_mbox);
}
if ($logout_expunge) {
$storage->expunge_folder('INBOX');
}
// Try to save unsaved user preferences
if (!empty($_SESSION['preferences'])) {
$this->user->save_prefs(unserialize($_SESSION['preferences']));
}
}
/**
* Build a valid URL to this instance of Roundcube
*
* @param mixed $p Either a string with the action or
* url parameters as key-value pairs
* @param boolean $absolute Build an URL absolute to document root
* @param boolean $full Create fully qualified URL including http(s):// and hostname
* @param bool $secure Return absolute URL in secure location
*
* @return string Valid application URL
*/
public function url($p, $absolute = false, $full = false, $secure = false)
{
if (!is_array($p)) {
if (strpos($p, 'http') === 0) {
return $p;
}
$p = array('_action' => @func_get_arg(0));
}
$pre = array();
$task = $p['_task'] ?: ($p['task'] ?: $this->task);
$pre['_task'] = $task;
unset($p['task'], $p['_task']);
$url = $this->filename;
$delm = '?';
foreach (array_merge($pre, $p) as $key => $val) {
if ($val !== '' && $val !== null) {
$par = $key[0] == '_' ? $key : '_'.$key;
$url .= $delm.urlencode($par).'='.urlencode($val);
$delm = '&';
}
}
$base_path = strval($_SERVER['REDIRECT_SCRIPT_URL'] ?: $_SERVER['SCRIPT_NAME']);
$base_path = preg_replace('![^/]+$!', '', $base_path);
if ($secure && ($token = $this->get_secure_url_token(true))) {
// add token to the url
$url = $token . '/' . $url;
// remove old token from the path
$base_path = rtrim($base_path, '/');
$base_path = preg_replace('/\/[a-zA-Z0-9]{' . strlen($token) . '}$/', '', $base_path);
// this need to be full url to make redirects work
$absolute = true;
}
else if ($secure && ($token = $this->get_request_token()))
$url .= $delm . '_token=' . urlencode($token);
if ($absolute || $full) {
// add base path to this Roundcube installation
if ($base_path == '') $base_path = '/';
$prefix = $base_path;
// prepend protocol://hostname:port
if ($full) {
$prefix = rcube_utils::resolve_url($prefix);
}
$prefix = rtrim($prefix, '/') . '/';
}
else {
$prefix = './';
}
return $prefix . $url;
}
/**
* Function to be executed in script shutdown
*/
public function shutdown()
{
parent::shutdown();
foreach ($this->address_books as $book) {
if (is_object($book) && is_a($book, 'rcube_addressbook')) {
$book->close();
}
}
// write performance stats to logs/console
if ($this->config->get('devel_mode') || $this->config->get('performance_stats')) {
// make sure logged numbers use unified format
setlocale(LC_NUMERIC, 'en_US.utf8', 'en_US.UTF-8', 'en_US', 'C');
if (function_exists('memory_get_usage')) {
$mem = $this->show_bytes(memory_get_usage());
}
if (function_exists('memory_get_peak_usage')) {
$mem .= '/'.$this->show_bytes(memory_get_peak_usage());
}
$log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : '');
if (defined('RCMAIL_START')) {
self::print_timer(RCMAIL_START, $log);
}
else {
self::console($log);
}
}
}
/**
* CSRF attack prevention code. Raises error when check fails.
*
* @param int $mode Request mode
*/
public function request_security_check($mode = rcube_utils::INPUT_POST)
{
// check request token
if (!$this->check_request($mode)) {
$error = array('code' => 403, 'message' => "Request security check failed");
self::raise_error($error, false, true);
}
-
- // check referer if configured
- if ($this->config->get('referer_check') && !rcube_utils::check_referer()) {
- $error = array('code' => 403, 'message' => "Referer check failed");
- self::raise_error($error, true, true);
- }
}
/**
* Registers action aliases for current task
*
* @param array $map Alias-to-filename hash array
*/
public function register_action_map($map)
{
if (is_array($map)) {
foreach ($map as $idx => $val) {
$this->action_map[$idx] = $val;
}
}
}
/**
* Returns current action filename
*
* @param array $map Alias-to-filename hash array
*/
public function get_action_file()
{
if (!empty($this->action_map[$this->action])) {
return $this->action_map[$this->action];
}
return strtr($this->action, '-', '_') . '.inc';
}
/**
* Fixes some user preferences according to namespace handling change.
* Old Roundcube versions were using folder names with removed namespace prefix.
* Now we need to add the prefix on servers where personal namespace has prefix.
*
* @param rcube_user $user User object
*/
private function fix_namespace_settings($user)
{
$prefix = $this->storage->get_namespace('prefix');
$prefix_len = strlen($prefix);
if (!$prefix_len) {
return;
}
if ($this->config->get('namespace_fixed')) {
return;
}
$prefs = array();
// Build namespace prefix regexp
$ns = $this->storage->get_namespace();
$regexp = array();
foreach ($ns as $entry) {
if (!empty($entry)) {
foreach ($entry as $item) {
if (strlen($item[0])) {
$regexp[] = preg_quote($item[0], '/');
}
}
}
}
$regexp = '/^('. implode('|', $regexp).')/';
// Fix preferences
$opts = array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox', 'archive_mbox');
foreach ($opts as $opt) {
if ($value = $this->config->get($opt)) {
if ($value != 'INBOX' && !preg_match($regexp, $value)) {
$prefs[$opt] = $prefix.$value;
}
}
}
if (($search_mods = $this->config->get('search_mods')) && !empty($search_mods)) {
$folders = array();
foreach ($search_mods as $idx => $value) {
if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) {
$idx = $prefix.$idx;
}
$folders[$idx] = $value;
}
$prefs['search_mods'] = $folders;
}
if (($threading = $this->config->get('message_threading')) && !empty($threading)) {
$folders = array();
foreach ($threading as $idx => $value) {
if ($idx != 'INBOX' && !preg_match($regexp, $idx)) {
$idx = $prefix.$idx;
}
$folders[$prefix.$idx] = $value;
}
$prefs['message_threading'] = $folders;
}
if ($collapsed = $this->config->get('collapsed_folders')) {
$folders = explode('&&', $collapsed);
$count = count($folders);
$folders_str = '';
if ($count) {
$folders[0] = substr($folders[0], 1);
$folders[$count-1] = substr($folders[$count-1], 0, -1);
}
foreach ($folders as $value) {
if ($value != 'INBOX' && !preg_match($regexp, $value)) {
$value = $prefix.$value;
}
$folders_str .= '&'.$value.'&';
}
$prefs['collapsed_folders'] = $folders_str;
}
$prefs['namespace_fixed'] = true;
// save updated preferences and reset imap settings (default folders)
$user->save_prefs($prefs);
$this->set_storage_prop();
}
/**
* Overwrite action variable
*
* @param string $action New action value
*/
public function overwrite_action($action)
{
$this->action = $action;
$this->output->set_env('action', $action);
}
/**
* Set environment variables for specified config options
*
* @param array $options List of configuration option names
*/
public function set_env_config($options)
{
foreach ((array) $options as $option) {
if ($this->config->get($option)) {
$this->output->set_env($option, true);
}
}
}
/**
* Returns RFC2822 formatted current date in user's timezone
*
* @return string Date
*/
public function user_date()
{
// get user's timezone
try {
$tz = new DateTimeZone($this->config->get('timezone'));
$date = new DateTime('now', $tz);
}
catch (Exception $e) {
$date = new DateTime();
}
return $date->format('r');
}
/**
* Write login data (name, ID, IP address) to the 'userlogins' log file.
*/
public function log_login($user = null, $failed_login = false, $error_code = 0)
{
if (!$this->config->get('log_logins')) {
return;
}
// failed login
if ($failed_login) {
// don't fill the log with complete input, which could
// have been prepared by a hacker
if (strlen($user) > 256) {
$user = substr($user, 0, 256) . '...';
}
$message = sprintf('Failed login for %s from %s in session %s (error: %d)',
$user, rcube_utils::remote_ip(), session_id(), $error_code);
}
// successful login
else {
$user_name = $this->get_user_name();
$user_id = $this->get_user_id();
if (!$user_id) {
return;
}
$message = sprintf('Successful login for %s (ID: %d) from %s in session %s',
$user_name, $user_id, rcube_utils::remote_ip(), session_id());
}
// log login
self::write_log('userlogins', $message);
}
/**
* Create a HTML table based on the given data
*
* @param array $attrib Named table attributes
* @param mixed $table_data Table row data. Either a two-dimensional array
* or a valid SQL result set
* @param array $show_cols List of cols to show
* @param string $id_col Name of the identifier col
*
* @return string HTML table code
*/
public function table_output($attrib, $table_data, $show_cols, $id_col)
{
$table = new html_table($attrib);
// add table header
if (!$attrib['noheader']) {
foreach ($show_cols as $col) {
$table->add_header($col, $this->Q($this->gettext($col)));
}
}
if (!is_array($table_data)) {
$db = $this->get_dbh();
while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) {
$table->add_row(array('id' => 'rcmrow' . rcube_utils::html_identifier($sql_arr[$id_col])));
// format each col
foreach ($show_cols as $col) {
$table->add($col, $this->Q($sql_arr[$col]));
}
}
}
else {
foreach ($table_data as $row_data) {
$class = !empty($row_data['class']) ? $row_data['class'] : null;
if (!empty($attrib['rowclass']))
$class = trim($class . ' ' . $attrib['rowclass']);
$rowid = 'rcmrow' . rcube_utils::html_identifier($row_data[$id_col]);
$table->add_row(array('id' => $rowid, 'class' => $class));
// format each col
foreach ($show_cols as $col) {
$val = is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col];
$table->add($col, empty($attrib['ishtml']) ? $this->Q($val) : $val);
}
}
}
return $table->show($attrib);
}
/**
* Convert the given date to a human readable form
* This uses the date formatting properties from config
*
* @param mixed $date Date representation (string, timestamp or DateTime object)
* @param string $format Date format to use
* @param bool $convert Enables date conversion according to user timezone
*
* @return string Formatted date string
*/
public function format_date($date, $format = null, $convert = true)
{
if (is_object($date) && is_a($date, 'DateTime')) {
$timestamp = $date->format('U');
}
else {
if (!empty($date)) {
$timestamp = rcube_utils::strtotime($date);
}
if (empty($timestamp)) {
return '';
}
try {
$date = new DateTime("@".$timestamp);
}
catch (Exception $e) {
return '';
}
}
if ($convert) {
try {
// convert to the right timezone
$stz = date_default_timezone_get();
$tz = new DateTimeZone($this->config->get('timezone'));
$date->setTimezone($tz);
date_default_timezone_set($tz->getName());
$timestamp = $date->format('U');
}
catch (Exception $e) {
}
}
// define date format depending on current time
if (!$format) {
$now = time();
$now_date = getdate($now);
$today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
$week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
$pretty_date = $this->config->get('prettydate');
if ($pretty_date && $timestamp > $today_limit && $timestamp <= $now) {
$format = $this->config->get('date_today', $this->config->get('time_format', 'H:i'));
$today = true;
}
else if ($pretty_date && $timestamp > $week_limit && $timestamp <= $now) {
$format = $this->config->get('date_short', 'D H:i');
}
else {
$format = $this->config->get('date_long', 'Y-m-d H:i');
}
}
// strftime() format
if (preg_match('/%[a-z]+/i', $format)) {
$format = strftime($format, $timestamp);
if ($stz) {
date_default_timezone_set($stz);
}
return $today ? ($this->gettext('today') . ' ' . $format) : $format;
}
// parse format string manually in order to provide localized weekday and month names
// an alternative would be to convert the date() format string to fit with strftime()
$out = '';
for ($i=0; $i<strlen($format); $i++) {
if ($format[$i] == "\\") { // skip escape chars
continue;
}
// write char "as-is"
if ($format[$i] == ' ' || $format[$i-1] == "\\") {
$out .= $format[$i];
}
// weekday (short)
else if ($format[$i] == 'D') {
$out .= $this->gettext(strtolower(date('D', $timestamp)));
}
// weekday long
else if ($format[$i] == 'l') {
$out .= $this->gettext(strtolower(date('l', $timestamp)));
}
// month name (short)
else if ($format[$i] == 'M') {
$out .= $this->gettext(strtolower(date('M', $timestamp)));
}
// month name (long)
else if ($format[$i] == 'F') {
$out .= $this->gettext('long'.strtolower(date('M', $timestamp)));
}
else if ($format[$i] == 'x') {
$out .= strftime('%x %X', $timestamp);
}
else {
$out .= date($format[$i], $timestamp);
}
}
if ($today) {
$label = $this->gettext('today');
// replcae $ character with "Today" label (#1486120)
if (strpos($out, '$') !== false) {
$out = preg_replace('/\$/', $label, $out, 1);
}
else {
$out = $label . ' ' . $out;
}
}
if ($stz) {
date_default_timezone_set($stz);
}
return $out;
}
/**
* Return folders list in HTML
*
* @param array $attrib Named parameters
*
* @return string HTML code for the gui object
*/
public function folder_list($attrib)
{
static $a_mailboxes;
$attrib += array('maxlength' => 100, 'realnames' => false, 'unreadwrap' => ' (%s)');
$type = $attrib['type'] ? $attrib['type'] : 'ul';
unset($attrib['type']);
if ($type == 'ul' && !$attrib['id']) {
$attrib['id'] = 'rcmboxlist';
}
if (empty($attrib['folder_name'])) {
$attrib['folder_name'] = '*';
}
// get current folder
$storage = $this->get_storage();
$mbox_name = $storage->get_folder();
// build the folders tree
if (empty($a_mailboxes)) {
// get mailbox list
$a_folders = $storage->list_folders_subscribed(
'', $attrib['folder_name'], $attrib['folder_filter']);
$delimiter = $storage->get_hierarchy_delimiter();
$a_mailboxes = array();
foreach ($a_folders as $folder) {
$this->build_folder_tree($a_mailboxes, $folder, $delimiter);
}
}
// allow plugins to alter the folder tree or to localize folder names
$hook = $this->plugins->exec_hook('render_mailboxlist', array(
'list' => $a_mailboxes,
'delimiter' => $delimiter,
'type' => $type,
'attribs' => $attrib,
));
$a_mailboxes = $hook['list'];
$attrib = $hook['attribs'];
if ($type == 'select') {
$attrib['is_escaped'] = true;
$select = new html_select($attrib);
// add no-selection option
if ($attrib['noselection']) {
$select->add(html::quote($this->gettext($attrib['noselection'])), '');
}
$this->render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']);
$out = $select->show($attrib['default']);
}
else {
$js_mailboxlist = array();
$tree = $this->render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib);
if ($type != 'js') {
$out = html::tag('ul', $attrib, $tree, html::$common_attrib);
$this->output->include_script('treelist.js');
$this->output->add_gui_object('mailboxlist', $attrib['id']);
$this->output->set_env('unreadwrap', $attrib['unreadwrap']);
$this->output->set_env('collapsed_folders', (string) $this->config->get('collapsed_folders'));
}
$this->output->set_env('mailboxes', $js_mailboxlist);
// we can't use object keys in javascript because they are unordered
// we need sorted folders list for folder-selector widget
$this->output->set_env('mailboxes_list', array_keys($js_mailboxlist));
}
// add some labels to client
$this->output->add_label('purgefolderconfirm', 'deletemessagesconfirm');
return $out;
}
/**
* Return folders list as html_select object
*
* @param array $p Named parameters
*
* @return html_select HTML drop-down object
*/
public function folder_selector($p = array())
{
$realnames = $this->config->get('show_real_foldernames');
$p += array('maxlength' => 100, 'realnames' => $realnames, 'is_escaped' => true);
$a_mailboxes = array();
$storage = $this->get_storage();
if (empty($p['folder_name'])) {
$p['folder_name'] = '*';
}
if ($p['unsubscribed']) {
$list = $storage->list_folders('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
}
else {
$list = $storage->list_folders_subscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
}
$delimiter = $storage->get_hierarchy_delimiter();
if (!empty($p['exceptions'])) {
$list = array_diff($list, (array) $p['exceptions']);
}
if (!empty($p['additional'])) {
foreach ($p['additional'] as $add_folder) {
$add_items = explode($delimiter, $add_folder);
$folder = '';
while (count($add_items)) {
$folder .= array_shift($add_items);
// @TODO: sorting
if (!in_array($folder, $list)) {
$list[] = $folder;
}
$folder .= $delimiter;
}
}
}
foreach ($list as $folder) {
$this->build_folder_tree($a_mailboxes, $folder, $delimiter);
}
$select = new html_select($p);
if ($p['noselection']) {
$select->add(html::quote($p['noselection']), '');
}
$this->render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p);
return $select;
}
/**
* Create a hierarchical array of the mailbox list
*/
public function build_folder_tree(&$arrFolders, $folder, $delm = '/', $path = '')
{
// Handle namespace prefix
$prefix = '';
if (!$path) {
$n_folder = $folder;
$folder = $this->storage->mod_folder($folder);
if ($n_folder != $folder) {
$prefix = substr($n_folder, 0, -strlen($folder));
}
}
$pos = strpos($folder, $delm);
if ($pos !== false) {
$subFolders = substr($folder, $pos+1);
$currentFolder = substr($folder, 0, $pos);
// sometimes folder has a delimiter as the last character
if (!strlen($subFolders)) {
$virtual = false;
}
else if (!isset($arrFolders[$currentFolder])) {
$virtual = true;
}
else {
$virtual = $arrFolders[$currentFolder]['virtual'];
}
}
else {
$subFolders = false;
$currentFolder = $folder;
$virtual = false;
}
$path .= $prefix . $currentFolder;
if (!isset($arrFolders[$currentFolder])) {
$arrFolders[$currentFolder] = array(
'id' => $path,
'name' => rcube_charset::convert($currentFolder, 'UTF7-IMAP'),
'virtual' => $virtual,
'folders' => array()
);
}
else {
$arrFolders[$currentFolder]['virtual'] = $virtual;
}
if (strlen($subFolders)) {
$this->build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
}
}
/**
* Return html for a structured list &lt;ul&gt; for the mailbox tree
*/
public function render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel = 0)
{
$maxlength = intval($attrib['maxlength']);
$realnames = (bool)$attrib['realnames'];
$msgcounts = $this->storage->get_cache('messagecount');
$collapsed = $this->config->get('collapsed_folders');
$realnames = $this->config->get('show_real_foldernames');
$out = '';
foreach ($arrFolders as $folder) {
$title = null;
$folder_class = $this->folder_classname($folder['id']);
$is_collapsed = strpos($collapsed, '&'.rawurlencode($folder['id']).'&') !== false;
$unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
if ($folder_class && !$realnames) {
$foldername = $this->gettext($folder_class);
}
else {
$foldername = $folder['name'];
// shorten the folder name to a given length
if ($maxlength && $maxlength > 1) {
$fname = abbreviate_string($foldername, $maxlength);
if ($fname != $foldername) {
$title = $foldername;
}
$foldername = $fname;
}
}
// make folder name safe for ids and class names
$folder_id = rcube_utils::html_identifier($folder['id'], true);
$classes = array('mailbox');
// set special class for Sent, Drafts, Trash and Junk
if ($folder_class) {
$classes[] = $folder_class;
}
if ($folder['id'] == $mbox_name) {
$classes[] = 'selected';
}
if ($folder['virtual']) {
$classes[] = 'virtual';
}
else if ($unread) {
$classes[] = 'unread';
}
$js_name = $this->JQ($folder['id']);
$html_name = $this->Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : '');
$link_attrib = $folder['virtual'] ? array() : array(
'href' => $this->url(array('_mbox' => $folder['id'])),
'onclick' => sprintf("return %s.command('list','%s',this,event)", rcmail_output::JS_OBJECT_NAME, $js_name),
'rel' => $folder['id'],
'title' => $title,
);
$out .= html::tag('li', array(
'id' => "rcmli" . $folder_id,
'class' => join(' ', $classes),
'noclose' => true
),
html::a($link_attrib, $html_name));
if (!empty($folder['folders'])) {
$out .= html::div('treetoggle ' . ($is_collapsed ? 'collapsed' : 'expanded'), '&nbsp;');
}
$jslist[$folder['id']] = array(
'id' => $folder['id'],
'name' => $foldername,
'virtual' => $folder['virtual'],
);
if (!empty($folder_class)) {
$jslist[$folder['id']]['class'] = $folder_class;
}
if (!empty($folder['folders'])) {
$out .= html::tag('ul', array('style' => ($is_collapsed ? "display:none;" : null)),
$this->render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1));
}
$out .= "</li>\n";
}
return $out;
}
/**
* Return html for a flat list <select> for the mailbox tree
*/
public function render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames = false, $nestLevel = 0, $opts = array())
{
$out = '';
foreach ($arrFolders as $folder) {
// skip exceptions (and its subfolders)
if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) {
continue;
}
// skip folders in which it isn't possible to create subfolders
if (!empty($opts['skip_noinferiors'])) {
$attrs = $this->storage->folder_attributes($folder['id']);
if ($attrs && in_array_nocase('\\Noinferiors', $attrs)) {
continue;
}
}
if (!$realnames && ($folder_class = $this->folder_classname($folder['id']))) {
$foldername = $this->gettext($folder_class);
}
else {
$foldername = $folder['name'];
// shorten the folder name to a given length
if ($maxlength && $maxlength > 1) {
$foldername = abbreviate_string($foldername, $maxlength);
}
}
$select->add(str_repeat('&nbsp;', $nestLevel*4) . html::quote($foldername), $folder['id']);
if (!empty($folder['folders'])) {
$out .= $this->render_folder_tree_select($folder['folders'], $mbox_name, $maxlength,
$select, $realnames, $nestLevel+1, $opts);
}
}
return $out;
}
/**
* Return internal name for the given folder if it matches the configured special folders
*/
public function folder_classname($folder_id)
{
if ($folder_id == 'INBOX') {
return 'inbox';
}
// for these mailboxes we have localized labels and css classes
foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx)
{
if ($folder_id === $this->config->get($smbx.'_mbox')) {
return $smbx;
}
}
}
/**
* Try to localize the given IMAP folder name.
* UTF-7 decode it in case no localized text was found
*
* @param string $name Folder name
* @param bool $with_path Enable path localization
* @param bool $path_remove Remove the path
*
* @return string Localized folder name in UTF-8 encoding
*/
public function localize_foldername($name, $with_path = false, $path_remove = false)
{
$realnames = $this->config->get('show_real_foldernames');
if (!$realnames && ($folder_class = $this->folder_classname($name))) {
return $this->gettext($folder_class);
}
$storage = $this->get_storage();
$delimiter = $storage->get_hierarchy_delimiter();
// Remove the path
if ($path_remove) {
if (strpos($name, $delimiter)) {
$path = explode($delimiter, $name);
$name = array_pop($path);
}
}
// try to localize path of the folder
else if ($with_path && !$realnames) {
$path = explode($delimiter, $name);
$count = count($path);
if ($count > 1) {
for ($i = 1; $i < $count; $i++) {
$folder = implode($delimiter, array_slice($path, 0, -$i));
if ($folder_class = $this->folder_classname($folder)) {
$name = implode($delimiter, array_slice($path, $count - $i));
$name = rcube_charset::convert($name, 'UTF7-IMAP');
return $this->gettext($folder_class) . $delimiter . $name;
}
}
}
}
return rcube_charset::convert($name, 'UTF7-IMAP');
}
/**
* Localize folder path
*/
public function localize_folderpath($path)
{
$protect_folders = $this->config->get('protect_default_folders');
$delimiter = $this->storage->get_hierarchy_delimiter();
$path = explode($delimiter, $path);
$result = array();
foreach ($path as $idx => $dir) {
$directory = implode($delimiter, array_slice($path, 0, $idx+1));
if ($protect_folders && $this->storage->is_special_folder($directory)) {
unset($result);
$result[] = $this->localize_foldername($directory);
}
else {
$result[] = rcube_charset::convert($dir, 'UTF7-IMAP');
}
}
return implode($delimiter, $result);
}
/**
* Return HTML for quota indicator object
*
* @param array $attrib Named parameters
*
* @return string HTML code for the quota indicator object
*/
public static function quota_display($attrib)
{
$rcmail = rcmail::get_instance();
if (!$attrib['id']) {
$attrib['id'] = 'rcmquotadisplay';
}
$_SESSION['quota_display'] = !empty($attrib['display']) ? $attrib['display'] : 'text';
$rcmail->output->add_gui_object('quotadisplay', $attrib['id']);
$quota = $rcmail->quota_content($attrib);
$rcmail->output->add_script('rcmail.set_quota('.rcube_output::json_serialize($quota).');', 'docready');
return html::span($attrib, '&nbsp;');
}
/**
* Return (parsed) quota information
*
* @param array $attrib Named parameters
* @param array $folder Current folder
*
* @return array Quota information
*/
public function quota_content($attrib = null, $folder = null)
{
$quota = $this->storage->get_quota($folder);
$quota = $this->plugins->exec_hook('quota', $quota);
$quota_result = (array) $quota;
$quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
$quota_result['folder'] = $folder !== null && $folder !== '' ? $folder : 'INBOX';
if ($quota['total'] > 0) {
if (!isset($quota['percent'])) {
$quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
}
$title = $this->gettext('quota') . ': ' . sprintf('%s / %s (%.0f%%)',
$this->show_bytes($quota['used'] * 1024),
$this->show_bytes($quota['total'] * 1024),
$quota_result['percent']
);
$quota_result['title'] = $title;
if ($attrib['width']) {
$quota_result['width'] = $attrib['width'];
}
if ($attrib['height']) {
$quota_result['height'] = $attrib['height'];
}
// build a table of quota types/roots info
if (($root_cnt = count($quota_result['all'])) > 1 || count($quota_result['all'][key($quota_result['all'])]) > 1) {
$table = new html_table(array('cols' => 3, 'class' => 'quota-info'));
$table->add_header(null, self::Q($this->gettext('quotatype')));
$table->add_header(null, self::Q($this->gettext('quotatotal')));
$table->add_header(null, self::Q($this->gettext('quotaused')));
foreach ($quota_result['all'] as $root => $data) {
if ($root_cnt > 1 && $root) {
$table->add(array('colspan' => 3, 'class' => 'root'), self::Q($root));
}
if ($storage = $data['storage']) {
$percent = min(100, round(($storage['used']/max(1,$storage['total']))*100));
$table->add('name', self::Q($this->gettext('quotastorage')));
$table->add(null, $this->show_bytes($storage['total'] * 1024));
$table->add(null, sprintf('%s (%.0f%%)', $this->show_bytes($storage['used'] * 1024), $percent));
}
if ($message = $data['message']) {
$percent = min(100, round(($message['used']/max(1,$message['total']))*100));
$table->add('name', self::Q($this->gettext('quotamessage')));
$table->add(null, intval($message['total']));
$table->add(null, sprintf('%d (%.0f%%)', $message['used'], $percent));
}
}
$quota_result['table'] = $table->show();
}
}
else {
$unlimited = $this->config->get('quota_zero_as_unlimited');
$quota_result['title'] = $this->gettext($unlimited ? 'unlimited' : 'unknown');
$quota_result['percent'] = 0;
}
// cleanup
unset($quota_result['abort']);
if (empty($quota_result['table'])) {
unset($quota_result['all']);
}
return $quota_result;
}
/**
* Outputs error message according to server error/response codes
*
* @param string $fallback Fallback message label
* @param array $fallback_args Fallback message label arguments
* @param string $suffix Message label suffix
* @param array $params Additional parameters (type, prefix)
*/
public function display_server_error($fallback = null, $fallback_args = null, $suffix = '', $params = array())
{
$err_code = $this->storage->get_error_code();
$res_code = $this->storage->get_response_code();
$args = array();
if ($res_code == rcube_storage::NOPERM) {
$error = 'errornoperm';
}
else if ($res_code == rcube_storage::READONLY) {
$error = 'errorreadonly';
}
else if ($res_code == rcube_storage::OVERQUOTA) {
$error = 'erroroverquota';
}
else if ($err_code && ($err_str = $this->storage->get_error_str())) {
// try to detect access rights problem and display appropriate message
if (stripos($err_str, 'Permission denied') !== false) {
$error = 'errornoperm';
}
// try to detect full mailbox problem and display appropriate message
// there can be e.g. "Quota exceeded" / "quotum would exceed" / "Over quota"
else if (stripos($err_str, 'quot') !== false && preg_match('/exceed|over/i', $err_str)) {
$error = 'erroroverquota';
}
else {
$error = 'servererrormsg';
$args = array('msg' => rcube::Q($err_str));
}
}
else if ($err_code < 0) {
$error = 'storageerror';
}
else if ($fallback) {
$error = $fallback;
$args = $fallback_args;
$params['prefix'] = false;
}
if ($error) {
if ($suffix && $this->text_exists($error . $suffix)) {
$error .= $suffix;
}
$msg = $this->gettext(array('name' => $error, 'vars' => $args));
if ($params['prefix'] && $fallback) {
$msg = $this->gettext(array('name' => $fallback, 'vars' => $fallback_args)) . ' ' . $msg;
}
$this->output->show_message($msg, $params['type'] ?: 'error');
}
}
/**
* Output HTML editor scripts
*
* @param string $mode Editor mode
*/
public function html_editor($mode = '')
{
$spellcheck = intval($this->config->get('enable_spellcheck'));
$spelldict = intval($this->config->get('spellcheck_dictionary'));
$disabled_plugins = array();
$disabled_buttons = array();
$extra_plugins = array();
$extra_buttons = array();
if (!$spellcheck) {
$disabled_plugins[] = 'spellchecker';
}
$hook = $this->plugins->exec_hook('html_editor', array(
'mode' => $mode,
'disabled_plugins' => $disabled_plugins,
'disabled_buttons' => $disabled_buttons,
'extra_plugins' => $extra_plugins,
'extra_buttons' => $extra_buttons,
));
if ($hook['abort']) {
return;
}
$lang_codes = array($_SESSION['language']);
$assets_dir = $this->config->get('assets_dir') ?: INSTALL_PATH;
if ($pos = strpos($_SESSION['language'], '_')) {
$lang_codes[] = substr($_SESSION['language'], 0, $pos);
}
foreach ($lang_codes as $code) {
if (file_exists("$assets_dir/program/js/tinymce/langs/$code.js")) {
$lang = $code;
break;
}
}
if (empty($lang)) {
$lang = 'en';
}
$config = array(
'mode' => $mode,
'lang' => $lang,
'skin_path' => $this->output->get_skin_path(),
'spellcheck' => $spellcheck, // deprecated
'spelldict' => $spelldict,
'disabled_plugins' => $hook['disabled_plugins'],
'disabled_buttons' => $hook['disabled_buttons'],
'extra_plugins' => $hook['extra_plugins'],
'extra_buttons' => $hook['extra_buttons'],
);
$this->output->add_label('selectimage', 'addimage', 'selectmedia', 'addmedia');
$this->output->set_env('editor_config', $config);
if ($path = $this->config->get('media_browser_css_location', 'program/resources/tinymce/browser.css')) {
if ($path != 'none') {
$this->output->include_css($path);
}
}
$this->output->include_script('tinymce/tinymce.min.js');
$this->output->include_script('editor.js');
}
/**
* File upload progress handler.
*/
public function upload_progress()
{
$params = array(
'action' => $this->action,
'name' => rcube_utils::get_input_value('_progress', rcube_utils::INPUT_GET),
);
if (function_exists('uploadprogress_get_info')) {
$status = uploadprogress_get_info($params['name']);
if (!empty($status)) {
$params['current'] = $status['bytes_uploaded'];
$params['total'] = $status['bytes_total'];
}
}
if (!isset($status) && filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN)
&& ini_get('apc.rfc1867_name')
) {
$prefix = ini_get('apc.rfc1867_prefix');
$status = apc_fetch($prefix . $params['name']);
if (!empty($status)) {
$params['current'] = $status['current'];
$params['total'] = $status['total'];
}
}
if (!isset($status) && filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN)
&& ini_get('session.upload_progress.name')
) {
$key = ini_get('session.upload_progress.prefix') . $params['name'];
$params['total'] = $_SESSION[$key]['content_length'];
$params['current'] = $_SESSION[$key]['bytes_processed'];
}
if (!empty($params['total'])) {
$total = $this->show_bytes($params['total'], $unit);
switch ($unit) {
case 'GB':
$gb = $params['current']/1073741824;
$current = sprintf($gb >= 10 ? "%d" : "%.1f", $gb);
break;
case 'MB':
$mb = $params['current']/1048576;
$current = sprintf($mb >= 10 ? "%d" : "%.1f", $mb);
break;
case 'KB':
$current = round($params['current']/1024);
break;
case 'B':
default:
$current = $params['current'];
break;
}
$params['percent'] = round($params['current']/$params['total']*100);
$params['text'] = $this->gettext(array(
'name' => 'uploadprogress',
'vars' => array(
'percent' => $params['percent'] . '%',
'current' => $current,
'total' => $total
)
));
}
$this->output->command('upload_progress_update', $params);
$this->output->send();
}
/**
* Initializes file uploading interface.
*
* @param int $max_size Optional maximum file size in bytes
*
* @return string Human-readable file size limit
*/
public function upload_init($max_size = null)
{
// Enable upload progress bar
if ($seconds = $this->config->get('upload_progress')) {
if (function_exists('uploadprogress_get_info')) {
$field_name = 'UPLOAD_IDENTIFIER';
}
if (!$field_name && filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN)) {
$field_name = ini_get('apc.rfc1867_name');
}
if (!$field_name && filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN)) {
$field_name = ini_get('session.upload_progress.name');
}
if ($field_name) {
$this->output->set_env('upload_progress_name', $field_name);
$this->output->set_env('upload_progress_time', (int) $seconds);
}
}
// find max filesize value
$max_filesize = rcube_utils::max_upload_size();
if ($max_size && $max_size < $max_filesize) {
$max_filesize = $max_size;
}
$max_filesize_txt = $this->show_bytes($max_filesize);
$this->output->set_env('max_filesize', $max_filesize);
$this->output->set_env('filesizeerror', $this->gettext(array(
'name' => 'filesizeerror', 'vars' => array('size' => $max_filesize_txt))));
if ($max_filecount = ini_get('max_file_uploads')) {
$this->output->set_env('max_filecount', $max_filecount);
$this->output->set_env('filecounterror', $this->gettext(array(
'name' => 'filecounterror', 'vars' => array('count' => $max_filecount))));
}
return $max_filesize_txt;
}
/**
* Upload form object
*
* @param array $attrib Object attributes
* @param string $name Form object name
* @param string $action Form action name
* @param array $input_attr File input attributes
*
* @return string HTML output
*/
public function upload_form($attrib, $name, $action, $input_attr = array())
{
// Get filesize, enable upload progress bar
$max_filesize = $this->upload_init();
$hint = html::div('hint', $this->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))));
if ($attrib['mode'] == 'hint') {
return $hint;
}
// set defaults
$attrib += array('id' => 'rcmUploadbox', 'buttons' => 'yes');
$event = rcmail_output::JS_OBJECT_NAME . ".command('$action', this.form)";
$form_id = $attrib['id'] . 'Frm';
// Default attributes of file input and form
$input_attr += array(
'id' => $attrib['id'] . 'Input',
'type' => 'file',
'name' => '_attachments[]',
);
$form_attr = array(
'id' => $form_id,
'name' => $name,
'method' => 'post',
'enctype' => 'multipart/form-data'
);
if ($attrib['mode'] == 'smart') {
unset($attrib['buttons']);
$form_attr['class'] = 'smart-upload';
$input_attr = array_merge($input_attr, array(
// #5854: Chrome does not execute onchange when selecting the same file.
// To fix this we reset the input using null value.
'onchange' => "$event; this.value=null",
'class' => 'smart-upload',
'tabindex' => '-1',
));
}
$input = new html_inputfield($input_attr);
$content = $attrib['prefix'] . $input->show();
if ($attrib['mode'] != 'smart') {
$content = html::div(null, $content);
$content .= $hint;
}
if (rcube_utils::get_boolean($attrib['buttons'])) {
$button = new html_inputfield(array('type' => 'button'));
$content .= html::div('buttons',
$button->show($this->gettext('close'), array('class' => 'button', 'onclick' => "$('#{$attrib['id']}').hide()")) . ' ' .
$button->show($this->gettext('upload'), array('class' => 'button mainaction', 'onclick' => $event))
);
}
$this->output->add_gui_object($name, $form_id);
return html::div($attrib, $this->output->form_tag($form_attr, $content));
}
/**
* Outputs uploaded file content (with image thumbnails support
*
* @param array $file Upload file data
*/
public function display_uploaded_file($file)
{
if (empty($file)) {
return;
}
$file = $this->plugins->exec_hook('attachment_display', $file);
if ($file['status']) {
if (empty($file['size'])) {
$file['size'] = $file['data'] ? strlen($file['data']) : @filesize($file['path']);
}
// generate image thumbnail for file browser in HTML editor
if (!empty($_GET['_thumbnail'])) {
$temp_dir = $this->config->get('temp_dir');
$thumbnail_size = 80;
$mimetype = $file['mimetype'];
$file_ident = $file['id'] . ':' . $file['mimetype'] . ':' . $file['size'];
$cache_basename = $temp_dir . '/' . md5($file_ident . ':' . $this->user->ID . ':' . $thumbnail_size);
$cache_file = $cache_basename . '.thumb';
// render thumbnail image if not done yet
if (!is_file($cache_file)) {
if (!$file['path']) {
$orig_name = $filename = $cache_basename . '.tmp';
file_put_contents($orig_name, $file['data']);
}
else {
$filename = $file['path'];
}
$image = new rcube_image($filename);
if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
$mimetype = 'image/' . $imgtype;
if ($orig_name) {
unlink($orig_name);
}
}
}
if (is_file($cache_file)) {
// cache for 1h
$this->output->future_expire_header(3600);
header('Content-Type: ' . $mimetype);
header('Content-Length: ' . filesize($cache_file));
readfile($cache_file);
exit;
}
}
header('Content-Type: ' . $file['mimetype']);
header('Content-Length: ' . $file['size']);
if ($file['data']) {
echo $file['data'];
}
else if ($file['path']) {
readfile($file['path']);
}
}
}
/**
* Initializes client-side autocompletion.
*/
public function autocomplete_init()
{
static $init;
if ($init) {
return;
}
$init = 1;
if (($threads = (int)$this->config->get('autocomplete_threads')) > 0) {
$book_types = (array) $this->config->get('autocomplete_addressbooks', 'sql');
if (count($book_types) > 1) {
$this->output->set_env('autocomplete_threads', $threads);
$this->output->set_env('autocomplete_sources', $book_types);
}
}
$this->output->set_env('autocomplete_max', (int)$this->config->get('autocomplete_max', 15));
$this->output->set_env('autocomplete_min_length', $this->config->get('autocomplete_min_length'));
$this->output->add_label('autocompletechars', 'autocompletemore');
}
/**
* Returns supported font-family specifications
*
* @param string $font Font name
*
* @param string|array Font-family specification array or string (if $font is used)
*/
public static function font_defs($font = null)
{
$fonts = array(
'Andale Mono' => '"Andale Mono",Times,monospace',
'Arial' => 'Arial,Helvetica,sans-serif',
'Arial Black' => '"Arial Black","Avant Garde",sans-serif',
'Book Antiqua' => '"Book Antiqua",Palatino,serif',
'Courier New' => '"Courier New",Courier,monospace',
'Georgia' => 'Georgia,Palatino,serif',
'Helvetica' => 'Helvetica,Arial,sans-serif',
'Impact' => 'Impact,Chicago,sans-serif',
'Tahoma' => 'Tahoma,Arial,Helvetica,sans-serif',
'Terminal' => 'Terminal,Monaco,monospace',
'Times New Roman' => '"Times New Roman",Times,serif',
'Trebuchet MS' => '"Trebuchet MS",Geneva,sans-serif',
'Verdana' => 'Verdana,Geneva,sans-serif',
);
if ($font) {
return $fonts[$font];
}
return $fonts;
}
/**
* Create a human readable string for a number of bytes
*
* @param int $bytes Number of bytes
* @param string &$unit Size unit
*
* @return string Byte string
*/
public function show_bytes($bytes, &$unit = null)
{
// Plugins may want to display different units
$plugin = $this->plugins->exec_hook('show_bytes', array('bytes' => $bytes));
$unit = $plugin['unit'];
if ($plugin['result'] !== null) {
return $plugin['result'];
}
if ($bytes >= 1073741824) {
$unit = 'GB';
$gb = $bytes/1073741824;
$str = sprintf($gb >= 10 ? "%d " : "%.1f ", $gb) . $this->gettext($unit);
}
else if ($bytes >= 1048576) {
$unit = 'MB';
$mb = $bytes/1048576;
$str = sprintf($mb >= 10 ? "%d " : "%.1f ", $mb) . $this->gettext($unit);
}
else if ($bytes >= 1024) {
$unit = 'KB';
$str = sprintf("%d ", round($bytes/1024)) . $this->gettext($unit);
}
else {
$unit = 'B';
$str = sprintf('%d ', $bytes) . $this->gettext($unit);
}
return $str;
}
/**
* Returns real size (calculated) of the message part
*
* @param rcube_message_part $part Message part
*
* @return string Part size (and unit)
*/
public function message_part_size($part)
{
if (isset($part->d_parameters['size'])) {
$size = $this->show_bytes((int)$part->d_parameters['size']);
}
else {
$size = $part->size;
if ($size === 0) {
$part->exact_size = true;
}
if ($part->encoding == 'base64') {
$size = $size / 1.33;
}
$size = $this->show_bytes($size);
}
if (!$part->exact_size) {
$size = '~' . $size;
}
return $size;
}
/**
* Returns message UID(s) and IMAP folder(s) from GET/POST data
*
* @param string $uids UID value to decode
* @param string $mbox Default mailbox value (if not encoded in UIDs)
* @param bool $is_multifolder Will be set to True if multi-folder request
* @param int $mode Request mode. Default: rcube_utils::INPUT_GPC.
*
* @return array List of message UIDs per folder
*/
public static function get_uids($uids = null, $mbox = null, &$is_multifolder = false, $mode = null)
{
// message UID (or comma-separated list of IDs) is provided in
// the form of <ID>-<MBOX>[,<ID>-<MBOX>]*
$_uid = $uids ?: rcube_utils::get_input_value('_uid', $mode ?: rcube_utils::INPUT_GPC);
$_mbox = $mbox ?: (string) rcube_utils::get_input_value('_mbox', $mode ?: rcube_utils::INPUT_GPC);
// already a hash array
if (is_array($_uid) && !isset($_uid[0])) {
return $_uid;
}
$result = array();
// special case: *
if ($_uid == '*' && is_object($_SESSION['search'][1]) && $_SESSION['search'][1]->multi) {
$is_multifolder = true;
// extract the full list of UIDs per folder from the search set
foreach ($_SESSION['search'][1]->sets as $subset) {
$mbox = $subset->get_parameters('MAILBOX');
$result[$mbox] = $subset->get();
}
}
else {
if (is_string($_uid)) {
$_uid = explode(',', $_uid);
}
// create a per-folder UIDs array
foreach ((array)$_uid as $uid) {
list($uid, $mbox) = explode('-', $uid, 2);
if (!strlen($mbox)) {
$mbox = $_mbox;
}
else {
$is_multifolder = true;
}
if ($uid == '*') {
$result[$mbox] = $uid;
}
else if (preg_match('/^[0-9:.]+$/', $uid)) {
$result[$mbox][] = $uid;
}
}
}
return $result;
}
/**
* Get resource file content (with assets_dir support)
*
* @param string $name File name
*
* @return string File content
*/
public function get_resource_content($name)
{
if (!strpos($name, '/')) {
$name = "program/resources/$name";
}
$assets_dir = $this->config->get('assets_dir');
if ($assets_dir) {
$path = slashify($assets_dir) . $name;
if (@file_exists($path)) {
$name = $path;
}
}
return file_get_contents($name, false);
}
/**
* Converts HTML content into plain text
*
* @param string $html HTML content
* @param array $options Conversion parameters (width, links, charset)
*
* @return string Plain text
*/
public function html2text($html, $options = array())
{
$default_options = array(
'links' => true,
'width' => 75,
'body' => $html,
'charset' => RCUBE_CHARSET,
);
$options = array_merge($default_options, (array) $options);
// Plugins may want to modify HTML in another/additional way
$options = $this->plugins->exec_hook('html2text', $options);
// Convert to text
if (!$options['abort']) {
$converter = new rcube_html2text($options['body'],
false, $options['links'], $options['width'], $options['charset']);
$options['body'] = rtrim($converter->get_text());
}
return $options['body'];
}
/**
* Connect to the mail storage server with stored session data
*
* @return bool True on success, False on error
*/
public function storage_connect()
{
$storage = $this->get_storage();
if ($_SESSION['storage_host'] && !$storage->is_connected()) {
$host = $_SESSION['storage_host'];
$user = $_SESSION['username'];
$port = $_SESSION['storage_port'];
$ssl = $_SESSION['storage_ssl'];
$pass = $this->decrypt($_SESSION['password']);
if (!$storage->connect($host, $user, $pass, $port, $ssl)) {
if (is_object($this->output)) {
$this->output->show_message('storageerror', 'error');
}
}
else {
$this->set_storage_prop();
}
}
return $storage->is_connected();
}
}
diff --git a/program/include/rcmail_install.php b/program/include/rcmail_install.php
index 4352dac0d..d8795dbb5 100644
--- a/program/include/rcmail_install.php
+++ b/program/include/rcmail_install.php
@@ -1,840 +1,840 @@
<?php
/**
+-----------------------------------------------------------------------+
| rcmail_install.php |
| |
| This file is part of the Roundcube Webmail package |
| Copyright (C) 2008-2018, 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. |
+-----------------------------------------------------------------------+
*/
/**
* Class to control the installation process of the Roundcube Webmail package
*
* @category Install
* @package Webmail
* @author Thomas Bruederli
*/
class rcmail_install
{
public $step;
public $last_error;
public $is_post = false;
public $failures = 0;
public $config = array();
public $configured = false;
public $legacy_config = false;
public $email_pattern = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9])';
public $bool_config_props = array();
public $local_config = array('db_dsnw', 'default_host', 'support_url', 'des_key', 'plugins');
- public $obsolete_config = array('db_backend', 'db_max_length', 'double_auth', 'preview_pane', 'debug_level');
+ public $obsolete_config = array('db_backend', 'db_max_length', 'double_auth', 'preview_pane', 'debug_level', 'referer_check');
public $replaced_config = array(
'skin_path' => 'skin',
'locale_string' => 'language',
'multiple_identities' => 'identities_level',
'addrbook_show_images' => 'show_images',
'imap_root' => 'imap_ns_personal',
'pagesize' => 'mail_pagesize',
'top_posting' => 'reply_mode',
'keep_alive' => 'refresh_interval',
'min_keep_alive' => 'min_refresh_interval',
);
// list of supported database drivers
public $supported_dbs = array(
'MySQL' => 'pdo_mysql',
'PostgreSQL' => 'pdo_pgsql',
'SQLite' => 'pdo_sqlite',
'SQLite (v2)' => 'pdo_sqlite2',
'SQL Server (SQLSRV)' => 'pdo_sqlsrv',
'SQL Server (DBLIB)' => 'pdo_dblib',
'Oracle' => 'oci8',
);
/**
* Constructor
*/
public function __construct()
{
$this->step = intval($_REQUEST['_step']);
$this->is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
}
/**
* Singleton getter
*/
public static function get_instance()
{
static $inst;
if (!$inst) {
$inst = new rcmail_install();
}
return $inst;
}
/**
* Read the local config files and store properties
*/
public function load_config()
{
// defaults
if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'defaults.inc.php')) {
$this->config = (array) $config;
$this->defaults = $this->config;
}
$config = null;
// config
if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'config.inc.php')) {
$this->config = array_merge($this->config, $config);
}
else {
if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'main.inc.php')) {
$this->config = array_merge($this->config, $config);
$this->legacy_config = true;
}
if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'db.inc.php')) {
$this->config = array_merge($this->config, $config);
$this->legacy_config = true;
}
}
$this->configured = !empty($config);
}
/**
* Read the default config file and store properties
*
* @param string $file File name with path
*/
public function load_config_file($file)
{
if (!is_readable($file)) {
return;
}
include $file;
// read comments from config file
if (function_exists('token_get_all')) {
$tokens = token_get_all(file_get_contents($file));
$in_config = false;
$buffer = '';
for ($i = 0; $i < count($tokens); $i++) {
$token = $tokens[$i];
if ($token[0] == T_VARIABLE && ($token[1] == '$config' || $token[1] == '$rcmail_config')) {
$in_config = true;
if ($buffer && $tokens[$i+1] == '[' && $tokens[$i+2][0] == T_CONSTANT_ENCAPSED_STRING) {
$propname = trim($tokens[$i+2][1], "'\"");
$this->comments[$propname] = $buffer;
$buffer = '';
$i += 3;
}
}
else if ($in_config && $token[0] == T_COMMENT) {
$buffer .= strtr($token[1], array('\n' => "\n"));
}
}
}
// deprecated name of config variable
if (is_array($rcmail_config)) {
return $rcmail_config;
}
return $config;
}
/**
* Getter for a certain config property
*
* @param string $name Property name
* @param string $default Default value
*
* @return string The property value
*/
public function getprop($name, $default = '')
{
$value = $this->config[$name];
if ($name == 'des_key' && !$this->configured && !isset($_REQUEST["_$name"])) {
$value = rcube_utils::random_bytes(24);
}
return $value !== null && $value !== '' ? $value : $default;
}
/**
* Create configuration file that contains parameters
* that differ from default values.
*
* @return string The complete config file content
*/
public function create_config()
{
$config = array();
foreach ($this->config as $prop => $default) {
$is_default = !isset($_POST["_$prop"]);
$value = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default;
// always disable installer
if ($prop == 'enable_installer') {
$value = false;
}
// reset useragent to default (keeps version up-to-date)
if ($prop == 'useragent' && stripos($value, 'Roundcube Webmail/') !== false) {
$value = $this->defaults[$prop];
}
// generate new encryption key, never use the default value
if ($prop == 'des_key' && $value == $this->defaults[$prop]) {
$value = rcube_utils::random_bytes(24);
}
// convert some form data
if ($prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
if ($_POST['_dbtype'] == 'sqlite') {
$value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'],
$_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
}
else if ($_POST['_dbtype']) {
$value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'],
rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']);
}
}
else if ($prop == 'smtp_auth_type' && $value == '0') {
$value = '';
}
else if ($prop == 'default_host' && is_array($value)) {
$value = self::_clean_array($value);
if (count($value) <= 1) {
$value = $value[0];
}
}
else if ($prop == 'mail_pagesize' || $prop == 'addressbook_pagesize') {
$value = max(2, intval($value));
}
else if ($prop == 'smtp_user' && !empty($_POST['_smtp_user_u'])) {
$value = '%u';
}
else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) {
$value = '%p';
}
else if (is_bool($default)) {
$value = (bool) $value;
}
else if (is_numeric($value)) {
$value = intval($value);
}
else if ($prop == 'plugins' && !empty($_POST['submit'])) {
$value = array();
foreach (array_keys($_POST) as $key) {
if (preg_match('/^_plugins_*/', $key)) {
array_push($value, $_POST[$key]);
}
}
}
// skip this property
if ($value == $this->defaults[$prop]
&& (!in_array($prop, $this->local_config)
|| in_array($prop, array_merge($this->obsolete_config, array_keys($this->replaced_config)))
|| preg_match('/^db_(table|sequence)_/', $prop)
)
) {
continue;
}
// save change
$this->config[$prop] = $value;
$config[$prop] = $value;
}
$out = "<?php\n\n";
$out .= "/* Local configuration for Roundcube Webmail */\n\n";
foreach ($config as $prop => $value) {
// copy option descriptions from existing config or defaults.inc.php
$out .= $this->comments[$prop];
$out .= "\$config['$prop'] = " . self::_dump_var($value, $prop) . ";\n\n";
}
return $out;
}
/**
* save generated config file in RCUBE_CONFIG_DIR
*
* @return boolean True if the file was saved successfully, false if not
*/
public function save_configfile($config)
{
if (is_writable(RCUBE_CONFIG_DIR)) {
return file_put_contents(RCUBE_CONFIG_DIR . 'config.inc.php', $config);
}
return false;
}
/**
* Check the current configuration for missing properties
* and deprecated or obsolete settings
*
* @return array List with problems detected
*/
public function check_config()
{
$this->load_config();
if (!$this->configured) {
return;
}
$out = $seen = array();
// iterate over the current configuration
foreach (array_keys($this->config) as $prop) {
if ($replacement = $this->replaced_config[$prop]) {
$out['replaced'][] = array('prop' => $prop, 'replacement' => $replacement);
$seen[$replacement] = true;
}
else if (!$seen[$prop] && in_array($prop, $this->obsolete_config)) {
$out['obsolete'][] = array('prop' => $prop);
$seen[$prop] = true;
}
}
// the old default mime_magic reference is obsolete
if ($this->config['mime_magic'] == '/usr/share/misc/magic') {
$out['obsolete'][] = array(
'prop' => 'mime_magic',
'explain' => "Set value to null in order to use system default"
);
}
// check config dependencies and contradictions
if ($this->config['enable_spellcheck'] && $this->config['spellcheck_engine'] == 'pspell') {
if (!extension_loaded('pspell')) {
$out['dependencies'][] = array(
'prop' => 'spellcheck_engine',
'explain' => "This requires the <tt>pspell</tt> extension which could not be loaded."
);
}
else if (!empty($this->config['spellcheck_languages'])) {
foreach ($this->config['spellcheck_languages'] as $lang => $descr) {
if (!@pspell_new($lang)) {
$out['dependencies'][] = array(
'prop' => 'spellcheck_languages',
'explain' => "You are missing pspell support for language $lang ($descr)"
);
}
}
}
}
if ($this->config['log_driver'] == 'syslog') {
if (!function_exists('openlog')) {
$out['dependencies'][] = array(
'prop' => 'log_driver',
'explain' => "This requires the <tt>syslog</tt> extension which could not be loaded."
);
}
if (empty($this->config['syslog_id'])) {
$out['dependencies'][] = array(
'prop' => 'syslog_id',
'explain' => "Using <tt>syslog</tt> for logging requires a syslog ID to be configured"
);
}
}
// check ldap_public sources having global_search enabled
if (is_array($this->config['ldap_public']) && !is_array($this->config['autocomplete_addressbooks'])) {
foreach ($this->config['ldap_public'] as $ldap_public) {
if ($ldap_public['global_search']) {
$out['replaced'][] = array(
'prop' => 'ldap_public::global_search',
'replacement' => 'autocomplete_addressbooks'
);
break;
}
}
}
return $out;
}
/**
* Merge the current configuration with the defaults
* and copy replaced values to the new options.
*/
public function merge_config()
{
$current = $this->config;
$this->config = array();
foreach ($this->replaced_config as $prop => $replacement) {
if (isset($current[$prop])) {
if ($prop == 'skin_path') {
$this->config[$replacement] = preg_replace('#skins/(\w+)/?$#', '\\1', $current[$prop]);
}
else if ($prop == 'multiple_identities') {
$this->config[$replacement] = $current[$prop] ? 2 : 0;
}
else {
$this->config[$replacement] = $current[$prop];
}
}
unset($current[$prop]);
}
foreach ($this->obsolete_config as $prop) {
unset($current[$prop]);
}
// add all ldap_public sources having global_search enabled to autocomplete_addressbooks
if (is_array($current['ldap_public'])) {
foreach ($current['ldap_public'] as $key => $ldap_public) {
if ($ldap_public['global_search']) {
$this->config['autocomplete_addressbooks'][] = $key;
unset($current['ldap_public'][$key]['global_search']);
}
}
}
$this->config = array_merge($this->config, $current);
foreach (array_keys((array) $current['ldap_public']) as $key) {
$this->config['ldap_public'][$key] = $current['ldap_public'][$key];
}
}
/**
* Compare the local database schema with the reference schema
* required for this version of Roundcube
*
* @param rcube_db $db Database object
*
* @return boolean True if the schema is up-to-date, false if not or an error occurred
*/
public function db_schema_check($db)
{
if (!$this->configured) {
return false;
}
// read reference schema from mysql.initial.sql
$engine = $db->db_provider;
$db_schema = $this->db_read_schema(INSTALL_PATH . "SQL/$engine.initial.sql");
$errors = array();
// check list of tables
$existing_tables = $db->list_tables();
foreach ($db_schema as $table => $cols) {
$table = $this->config['db_prefix'] . $table;
if (!in_array($table, $existing_tables)) {
$errors[] = "Missing table '".$table."'";
}
else { // compare cols
$db_cols = $db->list_cols($table);
$diff = array_diff(array_keys($cols), $db_cols);
if (!empty($diff)) {
$errors[] = "Missing columns in table '$table': " . join(',', $diff);
}
}
}
return !empty($errors) ? $errors : false;
}
/**
* Utility function to read database schema from an .sql file
*/
private function db_read_schema($schemafile)
{
$lines = file($schemafile);
$table_block = false;
$schema = array();
$keywords = array('PRIMARY','KEY','INDEX','UNIQUE','CONSTRAINT','REFERENCES','FOREIGN');
foreach ($lines as $line) {
if (preg_match('/^\s*create table ([\S]+)/i', $line, $m)) {
$table_name = explode('.', $m[1]);
$table_name = end($table_name);
$table_name = preg_replace('/[`"\[\]]/', '', $table_name);
}
else if ($table_name && ($line = trim($line))) {
if ($line == 'GO' || $line[0] == ')' || $line[strlen($line)-1] == ';') {
$table_name = null;
}
else {
$items = explode(' ', $line);
$col = $items[0];
$col = preg_replace('/[`"\[\]]/', '', $col);
if (!in_array(strtoupper($col), $keywords)) {
$type = strtolower($items[1]);
$type = preg_replace('/[^a-zA-Z0-9()]/', '', $type);
$schema[$table_name][$col] = $type;
}
}
}
}
return $schema;
}
/**
* Try to detect some file's mimetypes to test the correct behavior of fileinfo
*/
public function check_mime_detection()
{
$errors = array();
$files = array(
'skins/larry/images/roundcube_logo.png' => 'image/png',
'program/resources/blank.tiff' => 'image/tiff',
'program/resources/blocked.gif' => 'image/gif',
'skins/larry/README' => 'text/plain',
);
foreach ($files as $path => $expected) {
$mimetype = rcube_mime::file_content_type(INSTALL_PATH . $path, basename($path));
if ($mimetype != $expected) {
$errors[] = array($path, $mimetype, $expected);
}
}
return $errors;
}
/**
* Check the correct configuration of the 'mime_types' mapping option
*/
public function check_mime_extensions()
{
$errors = array();
$types = array(
'application/zip' => 'zip',
'text/css' => 'css',
'application/pdf' => 'pdf',
'image/gif' => 'gif',
'image/svg+xml' => 'svg',
);
foreach ($types as $mimetype => $expected) {
$ext = rcube_mime::get_mime_extensions($mimetype);
if (!in_array($expected, (array) $ext)) {
$errors[] = array($mimetype, $ext, $expected);
}
}
return $errors;
}
/**
* Getter for the last error message
*
* @return string Error message or null if none exists
*/
public function get_error()
{
return $this->last_error['message'];
}
/**
* Return a list with all imap hosts configured
*
* @return array Clean list with imap hosts
*/
public function get_hostlist()
{
$default_hosts = (array) $this->getprop('default_host');
$out = array();
foreach ($default_hosts as $key => $name) {
if (!empty($name)) {
$out[] = rcube_utils::parse_host(is_numeric($key) ? $name : $key);
}
}
return $out;
}
/**
* Create a HTML dropdown to select a previous version of Roundcube
*/
public function versions_select($attrib = array())
{
$select = new html_select($attrib);
$select->add(array(
'0.1-stable', '0.1.1',
'0.2-alpha', '0.2-beta', '0.2-stable',
'0.3-stable', '0.3.1',
'0.4-beta', '0.4.2',
'0.5-beta', '0.5', '0.5.1', '0.5.2', '0.5.3', '0.5.4',
'0.6-beta', '0.6',
'0.7-beta', '0.7', '0.7.1', '0.7.2', '0.7.3', '0.7.4',
'0.8-beta', '0.8-rc', '0.8.0', '0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5', '0.8.6',
'0.9-beta', '0.9-rc', '0.9-rc2',
// Note: Do not add newer versions here
));
return $select;
}
/**
* Return a list with available subfolders of the skin directory
*/
public function list_skins()
{
$skins = array();
$skindir = INSTALL_PATH . 'skins/';
foreach (glob($skindir . '*') as $path) {
if (is_dir($path) && is_readable($path)) {
$skins[] = substr($path, strlen($skindir));
}
}
return $skins;
}
/**
* Return a list with available subfolders of the plugins directory
* (with their associated description in composer.json)
*/
public function list_plugins()
{
$plugins = array();
$plugin_dir = INSTALL_PATH . 'plugins/';
foreach (glob($plugin_dir . '*') as $path) {
if (!is_dir($path)) {
continue;
}
if (is_readable($path . '/composer.json')) {
$file_json = json_decode(file_get_contents($path . '/composer.json'));
$plugin_desc = $file_json->description ?: 'N/A';
}
else {
$plugin_desc = 'N/A';
}
$name = substr($path, strlen($plugin_dir));
$plugins[] = array(
'name' => $name,
'desc' => $plugin_desc,
'enabled' => in_array($name, (array) $this->config['plugins'])
);
}
return $plugins;
}
/**
* Display OK status
*
* @param string $name Test name
* @param string $message Confirm message
*/
public function pass($name, $message = '')
{
echo rcube::Q($name) . ':&nbsp; <span class="success">OK</span>';
$this->_showhint($message);
}
/**
* Display an error status and increase failure count
*
* @param string $name Test name
* @param string $message Error message
* @param string $url URL for details
* @param bool $optional Do not count this failure
*/
public function fail($name, $message = '', $url = '', $optional=false)
{
if (!$optional) {
$this->failures++;
}
echo rcube::Q($name) . ':&nbsp; <span class="fail">NOT OK</span>';
$this->_showhint($message, $url);
}
/**
* Display an error status for optional settings/features
*
* @param string $name Test name
* @param string $message Error message
* @param string $url URL for details
*/
public function optfail($name, $message = '', $url = '')
{
echo rcube::Q($name) . ':&nbsp; <span class="na">NOT OK</span>';
$this->_showhint($message, $url);
}
/**
* Display warning status
*
* @param string $name Test name
* @param string $message Warning message
* @param string $url URL for details
*/
public function na($name, $message = '', $url = '')
{
echo rcube::Q($name) . ':&nbsp; <span class="na">NOT AVAILABLE</span>';
$this->_showhint($message, $url);
}
private function _showhint($message, $url = '')
{
$hint = rcube::Q($message);
if ($url) {
$hint .= ($hint ? '; ' : '') . 'See <a href="' . rcube::Q($url) . '" target="_blank">' . rcube::Q($url) . '</a>';
}
if ($hint) {
echo '<span class="indent">(' . $hint . ')</span>';
}
}
private static function _clean_array($arr)
{
$out = array();
foreach (array_unique($arr) as $k => $val) {
if (!empty($val)) {
if (is_numeric($k)) {
$out[] = $val;
}
else {
$out[$k] = $val;
}
}
}
return $out;
}
private static function _dump_var($var, $name=null)
{
// special values
switch ($name) {
case 'syslog_facility':
$list = array(32 => 'LOG_AUTH', 80 => 'LOG_AUTHPRIV', 72 => ' LOG_CRON',
24 => 'LOG_DAEMON', 0 => 'LOG_KERN', 128 => 'LOG_LOCAL0',
136 => 'LOG_LOCAL1', 144 => 'LOG_LOCAL2', 152 => 'LOG_LOCAL3',
160 => 'LOG_LOCAL4', 168 => 'LOG_LOCAL5', 176 => 'LOG_LOCAL6',
184 => 'LOG_LOCAL7', 48 => 'LOG_LPR', 16 => 'LOG_MAIL',
56 => 'LOG_NEWS', 40 => 'LOG_SYSLOG', 8 => 'LOG_USER', 64 => 'LOG_UUCP'
);
if ($val = $list[$var]) {
return $val;
}
break;
/*
// RCMAIL_VERSION is undefined here
case 'useragent':
if (preg_match('|^(.*)/('.preg_quote(RCMAIL_VERSION, '|').')$|i', $var, $m)) {
return '"' . addcslashes($var, '"') . '/" . RCMAIL_VERSION';
}
break;
*/
}
if (is_array($var)) {
if (empty($var)) {
return 'array()';
}
else { // check if all keys are numeric
$isnum = true;
foreach (array_keys($var) as $key) {
if (!is_numeric($key)) {
$isnum = false;
break;
}
}
if ($isnum) {
return 'array(' . join(', ', array_map(array('rcmail_install', '_dump_var'), $var)) . ')';
}
}
}
return var_export($var, true);
}
/**
* Initialize the database with the according schema
*
* @param rcube_db $db Database connection
*
* @return boolen True on success, False on error
*/
public function init_db($db)
{
$engine = $db->db_provider;
// read schema file from /SQL/*
$fname = INSTALL_PATH . "SQL/$engine.initial.sql";
if ($sql = @file_get_contents($fname)) {
$db->set_option('table_prefix', $this->config['db_prefix']);
$db->exec_script($sql);
}
else {
$this->fail('DB Schema', "Cannot read the schema file: $fname");
return false;
}
if ($err = $this->get_error()) {
$this->fail('DB Schema', "Error creating database schema: $err");
return false;
}
return true;
}
/**
* Update database schema
*
* @param string $version Version to update from
*
* @return boolen True on success, False on error
*/
public function update_db($version)
{
return rcmail_utils::db_update(INSTALL_PATH . 'SQL',
'roundcube', $version, array('quiet' => true));
}
/**
* Handler for Roundcube errors
*/
public function raise_error($p)
{
$this->last_error = $p;
}
}
diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php
index f010ff117..880b9c259 100644
--- a/program/lib/Roundcube/rcube_utils.php
+++ b/program/lib/Roundcube/rcube_utils.php
@@ -1,1366 +1,1353 @@
<?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: |
| Utility class providing common functions |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Utility class providing common functions
*
* @package Framework
* @subpackage Utils
*/
class rcube_utils
{
// define constants for input reading
const INPUT_GET = 1;
const INPUT_POST = 2;
const INPUT_COOKIE = 4;
const INPUT_GP = 3; // GET + POST
const INPUT_GPC = 7; // GET + POST + COOKIE
/**
* Helper method to set a cookie with the current path and host settings
*
* @param string Cookie name
* @param string Cookie value
* @param string Expiration time
*/
public static function setcookie($name, $value, $exp = 0)
{
if (headers_sent()) {
return;
}
$cookie = session_get_cookie_params();
$secure = $cookie['secure'] || self::https_check();
setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'], $secure, true);
}
/**
* E-mail address validation.
*
* @param string $email Email address
* @param boolean $dns_check True to check dns
*
* @return boolean True on success, False if address is invalid
*/
public static function check_email($email, $dns_check=true)
{
// Check for invalid (control) characters
if (preg_match('/\p{Cc}/u', $email)) {
return false;
}
// Check for length limit specified by RFC 5321 (#1486453)
if (strlen($email) > 254) {
return false;
}
$pos = strrpos($email, '@');
if (!$pos) {
return false;
}
$domain_part = substr($email, $pos + 1);
$local_part = substr($email, 0, $pos);
// quoted-string, make sure all backslashes and quotes are
// escaped
if (substr($local_part,0,1) == '"') {
$local_quoted = preg_replace('/\\\\(\\\\|\")/','', substr($local_part, 1, -1));
if (preg_match('/\\\\|"/', $local_quoted)) {
return false;
}
}
// dot-atom portion, make sure there's no prohibited characters
else if (preg_match('/(^\.|\.\.|\.$)/', $local_part)
|| preg_match('/[\\ ",:;<>@]/', $local_part)
) {
return false;
}
// Validate domain part
if (preg_match('/^\[((IPv6:[0-9a-f:.]+)|([0-9.]+))\]$/i', $domain_part, $matches)) {
return self::check_ip(preg_replace('/^IPv6:/i', '', $matches[1])); // valid IPv4 or IPv6 address
}
else {
// If not an IP address
$domain_array = explode('.', $domain_part);
// Not enough parts to be a valid domain
if (count($domain_array) < 2) {
return false;
}
foreach ($domain_array as $part) {
if (!preg_match('/^((xn--)?([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part)) {
return false;
}
}
// last domain part
$last_part = array_pop($domain_array);
if (strpos($last_part, 'xn--') !== 0 && preg_match('/[^a-zA-Z]/', $last_part)) {
return false;
}
$rcube = rcube::get_instance();
if (!$dns_check || !$rcube->config->get('email_dns_check')) {
return true;
}
// find MX record(s)
if (!function_exists('getmxrr') || getmxrr($domain_part, $mx_records)) {
return true;
}
// find any DNS record
if (!function_exists('checkdnsrr') || checkdnsrr($domain_part, 'ANY')) {
return true;
}
}
return false;
}
/**
* Validates IPv4 or IPv6 address
*
* @param string $ip IP address in v4 or v6 format
*
* @return bool True if the address is valid
*/
public static function check_ip($ip)
{
return filter_var($ip, FILTER_VALIDATE_IP) !== false;
}
- /**
- * Check whether the HTTP referer matches the current request
- *
- * @return boolean True if referer is the same host+path, false if not
- */
- public static function check_referer()
- {
- $uri = parse_url($_SERVER['REQUEST_URI']);
- $referer = parse_url(self::request_header('Referer'));
-
- return $referer['host'] == self::request_header('Host') && $referer['path'] == $uri['path'];
- }
-
/**
* Replacing specials characters to a specific encoding type
*
* @param string Input string
* @param string Encoding type: text|html|xml|js|url
* @param string Replace mode for tags: show|remove|strict
* @param boolean Convert newlines
*
* @return string The quoted string
*/
public static function rep_specialchars_output($str, $enctype = '', $mode = '', $newlines = true)
{
static $html_encode_arr = false;
static $js_rep_table = false;
static $xml_rep_table = false;
if (!is_string($str)) {
$str = strval($str);
}
// encode for HTML output
if ($enctype == 'html') {
if (!$html_encode_arr) {
$html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);
unset($html_encode_arr['?']);
}
$encode_arr = $html_encode_arr;
if ($mode == 'remove') {
$str = strip_tags($str);
}
else if ($mode != 'strict') {
// don't replace quotes and html tags
$ltpos = strpos($str, '<');
if ($ltpos !== false && strpos($str, '>', $ltpos) !== false) {
unset($encode_arr['"']);
unset($encode_arr['<']);
unset($encode_arr['>']);
unset($encode_arr['&']);
}
}
$out = strtr($str, $encode_arr);
return $newlines ? nl2br($out) : $out;
}
// if the replace tables for XML and JS are not yet defined
if ($js_rep_table === false) {
$js_rep_table = $xml_rep_table = array();
$xml_rep_table['&'] = '&amp;';
// can be increased to support more charsets
for ($c=160; $c<256; $c++) {
$xml_rep_table[chr($c)] = "&#$c;";
}
$xml_rep_table['"'] = '&quot;';
$js_rep_table['"'] = '\\"';
$js_rep_table["'"] = "\\'";
$js_rep_table["\\"] = "\\\\";
// Unicode line and paragraph separators (#1486310)
$js_rep_table[chr(hexdec('E2')).chr(hexdec('80')).chr(hexdec('A8'))] = '&#8232;';
$js_rep_table[chr(hexdec('E2')).chr(hexdec('80')).chr(hexdec('A9'))] = '&#8233;';
}
// encode for javascript use
if ($enctype == 'js') {
return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table));
}
// encode for plaintext
if ($enctype == 'text') {
return str_replace("\r\n", "\n", $mode == 'remove' ? strip_tags($str) : $str);
}
if ($enctype == 'url') {
return rawurlencode($str);
}
// encode for XML
if ($enctype == 'xml') {
return strtr($str, $xml_rep_table);
}
// no encoding given -> return original string
return $str;
}
/**
* Read input value and convert it for internal use
* Performs stripslashes() and charset conversion if necessary
*
* @param string Field name to read
* @param int Source to get value from (see self::INPUT_*)
* @param boolean Allow HTML tags in field value
* @param string Charset to convert into
*
* @return string Field value or NULL if not available
*/
public static function get_input_value($fname, $source, $allow_html = false, $charset = null)
{
$value = null;
if (($source & self::INPUT_GET) && isset($_GET[$fname])) {
$value = $_GET[$fname];
}
if (($source & self::INPUT_POST) && isset($_POST[$fname])) {
$value = $_POST[$fname];
}
if (($source & self::INPUT_COOKIE) && isset($_COOKIE[$fname])) {
$value = $_COOKIE[$fname];
}
return self::parse_input_value($value, $allow_html, $charset);
}
/**
* Parse/validate input value. See self::get_input_value()
* Performs stripslashes() and charset conversion if necessary
*
* @param string Input value
* @param boolean Allow HTML tags in field value
* @param string Charset to convert into
*
* @return string Parsed value
*/
public static function parse_input_value($value, $allow_html = false, $charset = null)
{
global $OUTPUT;
if (empty($value)) {
return $value;
}
if (is_array($value)) {
foreach ($value as $idx => $val) {
$value[$idx] = self::parse_input_value($val, $allow_html, $charset);
}
return $value;
}
// remove HTML tags if not allowed
if (!$allow_html) {
$value = strip_tags($value);
}
$output_charset = is_object($OUTPUT) ? $OUTPUT->get_charset() : null;
// remove invalid characters (#1488124)
if ($output_charset == 'UTF-8') {
$value = rcube_charset::clean($value);
}
// convert to internal charset
if ($charset && $output_charset) {
$value = rcube_charset::convert($value, $output_charset, $charset);
}
return $value;
}
/**
* Convert array of request parameters (prefixed with _)
* to a regular array with non-prefixed keys.
*
* @param int $mode Source to get value from (GPC)
* @param string $ignore PCRE expression to skip parameters by name
* @param boolean $allow_html Allow HTML tags in field value
*
* @return array Hash array with all request parameters
*/
public static function request2param($mode = null, $ignore = 'task|action', $allow_html = false)
{
$out = array();
$src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST);
foreach (array_keys($src) as $key) {
$fname = $key[0] == '_' ? substr($key, 1) : $key;
if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) {
$out[$fname] = self::get_input_value($key, $mode, $allow_html);
}
}
return $out;
}
/**
* Convert the given string into a valid HTML identifier
* Same functionality as done in app.js with rcube_webmail.html_identifier()
*/
public static function html_identifier($str, $encode=false)
{
if ($encode) {
return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
}
else {
return asciiwords($str, true, '_');
}
}
/**
* Replace all css definitions with #container [def]
* and remove css-inlined scripting, make position style safe
*
* @param string CSS source code
* @param string Container ID to use as prefix
* @param bool Allow remote content
* @param string Prefix to be added to id/class identifier
*
* @return string Modified CSS source
*/
public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '')
{
$last_pos = 0;
$replacements = new rcube_string_replacer;
// ignore the whole block if evil styles are detected
$source = self::xss_entity_decode($source);
$stripped = preg_replace('/[^a-z\(:;]/i', '', $source);
$evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\((?!data:image)' : '');
if (preg_match("/$evilexpr/i", $stripped)) {
return '/* evil! */';
}
$strict_url_regexp = '!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims';
// cut out all contents between { and }
while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) {
$nested = strpos($source, '{', $pos+1);
if ($nested && $nested < $pos2) // when dealing with nested blocks (e.g. @media), take the inner one
$pos = $nested;
$length = $pos2 - $pos - 1;
$styles = substr($source, $pos+1, $length);
// Convert position:fixed to position:absolute (#5264)
$styles = preg_replace('/position:[\s\r\n]*fixed/i', 'position: absolute', $styles);
// check every line of a style block...
if ($allow_remote) {
$a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY);
for ($i=0, $len=count($a_styles); $i < $len; $i++) {
$line = $a_styles[$i];
$stripped = preg_replace('/[^a-z\(:;]/i', '', $line);
// allow data:image uri, join with continuation
if (stripos($stripped, 'url(data:image')) {
$a_styles[$i] .= ';' . $a_styles[$i+1];
unset($a_styles[$i+1]);
}
// allow strict url() values only
else if (stripos($stripped, 'url(') && !preg_match($strict_url_regexp, $line)) {
$a_styles = array('/* evil! */');
break;
}
}
$styles = join(";\n", $a_styles);
}
$key = $replacements->add($styles);
$repl = $replacements->get_replacement($key);
$source = substr_replace($source, $repl, $pos+1, $length);
$last_pos = $pos2 - ($length - strlen($repl));
}
// remove html comments
$source = preg_replace('/(^\s*<\!--)|(-->\s*$)/m', '', $source);
// add #container to each tag selector and prefix to id/class identifiers
if ($container_id || $prefix) {
// (?!##str) below is to not match with ##str_replacement_0##
// from rcube_string_replacer used above, this is needed for
// cases like @media { body { position: fixed; } } (#5811)
$regexp = '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str)[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im';
$callback = function($matches) use ($container_id, $prefix) {
$replace = $matches[2];
if ($prefix) {
$replace = str_replace(array('.', '#'), array(".$prefix", "#$prefix"), $replace);
}
if ($container_id) {
$replace = "#$container_id " . $replace;
}
return str_replace($matches[2], $replace, $matches[0]);
};
$source = preg_replace_callback($regexp, $callback, $source);
}
// replace body definition because we also stripped off the <body> tag
if ($container_id) {
$regexp = '/#' . preg_quote($container_id, '/') . '\s+body/i';
$source = preg_replace($regexp, "#$container_id", $source);
}
// put block contents back in
$source = $replacements->resolve($source);
return $source;
}
/**
* Generate CSS classes from mimetype and filename extension
*
* @param string $mimetype Mimetype
* @param string $filename Filename
*
* @return string CSS classes separated by space
*/
public static function file2class($mimetype, $filename)
{
$mimetype = strtolower($mimetype);
$filename = strtolower($filename);
list($primary, $secondary) = explode('/', $mimetype);
$classes = array($primary ?: 'unknown');
if ($secondary) {
$classes[] = $secondary;
}
if (preg_match('/\.([a-z0-9]+)$/', $filename, $m)) {
if (!in_array($m[1], $classes)) {
$classes[] = $m[1];
}
}
return join(" ", $classes);
}
/**
* Decode escaped entities used by known XSS exploits.
* See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples
*
* @param string CSS content to decode
*
* @return string Decoded string
*/
public static function xss_entity_decode($content)
{
$callback = function($matches) { return chr(hexdec($matches[1])); };
$out = html_entity_decode(html_entity_decode($content));
$out = trim(preg_replace('/(^<!--|-->$)/', '', trim($out)));
$out = preg_replace_callback('/\\\([0-9a-f]{2,6})\s*/i', $callback, $out);
$out = preg_replace('/\\\([^0-9a-f])/i', '\\1', $out);
$out = preg_replace('#/\*.*\*/#Ums', '', $out);
$out = strip_tags($out);
return $out;
}
/**
* Check if we can process not exceeding memory_limit
*
* @param integer Required amount of memory
*
* @return boolean True if memory won't be exceeded, False otherwise
*/
public static function mem_check($need)
{
$mem_limit = parse_bytes(ini_get('memory_limit'));
$memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true;
}
/**
* Check if working in SSL mode
*
* @param integer $port HTTPS port number
* @param boolean $use_https Enables 'use_https' option checking
*
* @return boolean
*/
public static function https_check($port=null, $use_https=true)
{
if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') {
return true;
}
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
&& in_array($_SERVER['REMOTE_ADDR'], rcube::get_instance()->config->get('proxy_whitelist', array()))
) {
return true;
}
if ($port && $_SERVER['SERVER_PORT'] == $port) {
return true;
}
if ($use_https && rcube::get_instance()->config->get('use_https')) {
return true;
}
return false;
}
/**
* Replaces hostname variables.
*
* @param string $name Hostname
* @param string $host Optional IMAP hostname
*
* @return string Hostname
*/
public static function parse_host($name, $host = '')
{
if (!is_string($name)) {
return $name;
}
// %n - host
$n = self::server_name();
// %t - host name without first part, e.g. %n=mail.domain.tld, %t=domain.tld
$t = preg_replace('/^[^\.]+\./', '', $n);
// %d - domain name without first part
$d = preg_replace('/^[^\.]+\./', '', self::server_name('HTTP_HOST'));
// %h - IMAP host
$h = $_SESSION['storage_host'] ?: $host;
// %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld
$z = preg_replace('/^[^\.]+\./', '', $h);
// %s - domain name after the '@' from e-mail address provided at login screen.
// Returns FALSE if an invalid email is provided
if (strpos($name, '%s') !== false) {
$user_email = self::idn_to_ascii(self::get_input_value('_user', self::INPUT_POST));
$matches = preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s);
if ($matches < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false) {
return false;
}
}
return str_replace(array('%n', '%t', '%d', '%h', '%z', '%s'), array($n, $t, $d, $h, $z, $s[2]), $name);
}
/**
* Returns the host name after checking it against trusted hostname
* patterns, otherwise returns localhost (and logs a warning)
*
* @param string $type The $_SERVER key, e.g. 'HTTP_HOST', Default: 'SERVER_NAME'.
* @param boolean $strip_port Strip port from the host name
*
* @return string Server name
*/
public static function server_name($type = null, $strip_port = true)
{
$name = $_SERVER[$type ?: 'SERVER_NAME'];
$rcube = rcube::get_instance();
$patterns = (array) $rcube->config->get('trusted_host_patterns');
if ($strip_port) {
$name = preg_replace('/:\d+$/', '', $name);
}
if (empty($patterns) || in_array_nocase($name, $patterns)) {
return $name;
}
if (!empty($name)) {
foreach ($patterns as $pattern) {
if (preg_match("/$pattern/", $name)) {
return $name;
}
}
$rcube->raise_error(array('file' => __FILE__, 'line' => __LINE__,
'message' => "Specified host is not trusted. Using 'localhost'."), true, false);
}
return 'localhost';
}
/**
* Returns remote IP address and forwarded addresses if found
*
* @return string Remote IP address(es)
*/
public static function remote_ip()
{
$address = $_SERVER['REMOTE_ADDR'];
// append the NGINX X-Real-IP header, if set
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP'];
}
// append the X-Forwarded-For header, if set
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if (!empty($remote_ip)) {
$address .= '(' . implode(',', $remote_ip) . ')';
}
return $address;
}
/**
* Returns the real remote IP address
*
* @return string Remote IP address
*/
public static function remote_addr()
{
// Check if any of the headers are set first to improve performance
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) || !empty($_SERVER['HTTP_X_REAL_IP'])) {
$proxy_whitelist = rcube::get_instance()->config->get('proxy_whitelist', array());
if (in_array($_SERVER['REMOTE_ADDR'], $proxy_whitelist)) {
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
foreach (array_reverse(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])) as $forwarded_ip) {
if (!in_array($forwarded_ip, $proxy_whitelist)) {
return $forwarded_ip;
}
}
}
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
return $_SERVER['HTTP_X_REAL_IP'];
}
}
}
if (!empty($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return '';
}
/**
* Read a specific HTTP request header.
*
* @param string $name Header name
*
* @return mixed Header value or null if not available
*/
public static function request_header($name)
{
if (function_exists('getallheaders')) {
$hdrs = array_change_key_case(getallheaders(), CASE_UPPER);
$key = strtoupper($name);
}
else {
$key = 'HTTP_' . strtoupper(strtr($name, '-', '_'));
$hdrs = array_change_key_case($_SERVER, CASE_UPPER);
}
return $hdrs[$key];
}
/**
* Explode quoted string
*
* @param string Delimiter expression string for preg_match()
* @param string Input string
*
* @return array String items
*/
public static function explode_quoted_string($delimiter, $string)
{
$result = array();
$strlen = strlen($string);
for ($q=$p=$i=0; $i < $strlen; $i++) {
if ($string[$i] == "\"" && $string[$i-1] != "\\") {
$q = $q ? false : true;
}
else if (!$q && preg_match("/$delimiter/", $string[$i])) {
$result[] = substr($string, $p, $i - $p);
$p = $i + 1;
}
}
$result[] = (string) substr($string, $p);
return $result;
}
/**
* Improved equivalent to strtotime()
*
* @param string $date Date string
* @param DateTimeZone $timezone Timezone to use for DateTime object
*
* @return int Unix timestamp
*/
public static function strtotime($date, $timezone = null)
{
$date = self::clean_datestr($date);
$tzname = $timezone ? ' ' . $timezone->getName() : '';
// unix timestamp
if (is_numeric($date)) {
return (int) $date;
}
// It can be very slow when provided string is not a date and very long
if (strlen($date) > 128) {
$date = substr($date, 0, 128);
}
// if date parsing fails, we have a date in non-rfc format.
// remove token from the end and try again
while (($ts = @strtotime($date . $tzname)) === false || $ts < 0) {
if (($pos = strrpos($date, ' ')) === false) {
break;
}
$date = rtrim(substr($date, 0, $pos));
}
return (int) $ts;
}
/**
* Date parsing function that turns the given value into a DateTime object
*
* @param string $date Date string
* @param DateTimeZone $timezone Timezone to use for DateTime object
*
* @return DateTime instance or false on failure
*/
public static function anytodatetime($date, $timezone = null)
{
if ($date instanceof DateTime) {
return $date;
}
$dt = false;
$date = self::clean_datestr($date);
// try to parse string with DateTime first
if (!empty($date)) {
try {
$_date = preg_match('/^[0-9]+$/', $date) ? "@$date" : $date;
$dt = $timezone ? new DateTime($_date, $timezone) : new DateTime($_date);
}
catch (Exception $e) {
// ignore
}
}
// try our advanced strtotime() method
if (!$dt && ($timestamp = self::strtotime($date, $timezone))) {
try {
$dt = new DateTime("@".$timestamp);
if ($timezone) {
$dt->setTimezone($timezone);
}
}
catch (Exception $e) {
// ignore
}
}
return $dt;
}
/**
* Clean up date string for strtotime() input
*
* @param string $date Date string
*
* @return string Date string
*/
public static function clean_datestr($date)
{
$date = trim($date);
// check for MS Outlook vCard date format YYYYMMDD
if (preg_match('/^([12][90]\d\d)([01]\d)([0123]\d)$/', $date, $m)) {
return sprintf('%04d-%02d-%02d 00:00:00', intval($m[1]), intval($m[2]), intval($m[3]));
}
// Clean malformed data
$date = preg_replace(
array(
'/\(.*\)/', // remove RFC comments
'/GMT\s*([+-][0-9]+)/', // support non-standard "GMTXXXX" literal
'/[^a-z0-9\x20\x09:\/\.+-]/i', // remove any invalid characters
'/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*/i', // remove weekday names
),
array(
'',
'\\1',
'',
'',
), $date);
$date = trim($date);
// try to fix dd/mm vs. mm/dd discrepancy, we can't do more here
if (preg_match('/^(\d{1,2})[.\/-](\d{1,2})[.\/-](\d{4})(\s.*)?$/', $date, $m)) {
$mdy = $m[2] > 12 && $m[1] <= 12;
$day = $mdy ? $m[2] : $m[1];
$month = $mdy ? $m[1] : $m[2];
$date = sprintf('%04d-%02d-%02d%s', $m[3], $month, $day, $m[4] ?: ' 00:00:00');
}
// I've found that YYYY.MM.DD is recognized wrong, so here's a fix
else if (preg_match('/^(\d{4})\.(\d{1,2})\.(\d{1,2})(\s.*)?$/', $date, $m)) {
$date = sprintf('%04d-%02d-%02d%s', $m[1], $m[2], $m[3], $m[4] ?: ' 00:00:00');
}
return $date;
}
/**
* Turns the given date-only string in defined format into YYYY-MM-DD format.
*
* Supported formats: 'Y/m/d', 'Y.m.d', 'd-m-Y', 'd/m/Y', 'd.m.Y', 'j.n.Y'
*
* @param string $date Date string
* @param string $format Input date format
*
* @return strin Date string in YYYY-MM-DD format, or the original string
* if format is not supported
*/
public static function format_datestr($date, $format)
{
$format_items = preg_split('/[.-\/\\\\]/', $format);
$date_items = preg_split('/[.-\/\\\\]/', $date);
$iso_format = '%04d-%02d-%02d';
if (count($format_items) == 3 && count($date_items) == 3) {
if ($format_items[0] == 'Y') {
$date = sprintf($iso_format, $date_items[0], $date_items[1], $date_items[2]);
}
else if (strpos('dj', $format_items[0]) !== false) {
$date = sprintf($iso_format, $date_items[2], $date_items[1], $date_items[0]);
}
else if (strpos('mn', $format_items[0]) !== false) {
$date = sprintf($iso_format, $date_items[2], $date_items[0], $date_items[1]);
}
}
return $date;
}
/**
* Wrapper for idn_to_ascii with support for e-mail address.
*
* Warning: Domain names may be lowercase'd.
* Warning: An empty string may be returned on invalid domain.
*
* @param string $str Decoded e-mail address
*
* @return string Encoded e-mail address
*/
public static function idn_to_ascii($str)
{
return self::idn_convert($str, true);
}
/**
* Wrapper for idn_to_utf8 with support for e-mail address
*
* @param string $str Decoded e-mail address
*
* @return string Encoded e-mail address
*/
public static function idn_to_utf8($str)
{
return self::idn_convert($str, false);
}
/**
* Convert a string to ascii or utf8 (using IDNA standard)
*
* @param string $input Decoded e-mail address
* @param boolean $is_utf Convert by idn_to_ascii if true and idn_to_utf8 if false
*
* @return string Encoded e-mail address
*/
public static function idn_convert($input, $is_utf = false)
{
if ($at = strpos($input, '@')) {
$user = substr($input, 0, $at);
$domain = substr($input, $at + 1);
}
else {
$user = '';
$domain = $input;
}
// Note that in PHP 7.2/7.3 calling idn_to_* functions with default arguments
// throws a warning, so we have to set the variant explicitely (#6075)
$variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : null;
$options = 0;
// Because php-intl extension lowercases domains and return false
// on invalid input (#6224), we skip conversion when not needed
// for compatibility with our Net_IDNA2 wrappers in bootstrap.php
if ($is_utf) {
if (preg_match('/[^\x20-\x7E]/', $domain)) {
$domain = idn_to_ascii($domain, $options, $variant);
}
}
else if (preg_match('/(^|\.)xn--/i', $domain)) {
$domain = idn_to_utf8($domain, $options, $variant);
}
if ($domain === false) {
return '';
}
return $at ? $user . '@' . $domain : $domain;
}
/**
* Split the given string into word tokens
*
* @param string Input to tokenize
* @param integer Minimum length of a single token
* @return array List of tokens
*/
public static function tokenize_string($str, $minlen = 2)
{
$expr = array('/[\s;,"\'\/+-]+/ui', '/(\d)[-.\s]+(\d)/u');
$repl = array(' ', '\\1\\2');
if ($minlen > 1) {
$minlen--;
$expr[] = "/(^|\s+)\w{1,$minlen}(\s+|$)/u";
$repl[] = ' ';
}
return array_filter(explode(" ", preg_replace($expr, $repl, $str)));
}
/**
* Normalize the given string for fulltext search.
* Currently only optimized for ISO-8859-1 and ISO-8859-2 characters; to be extended
*
* @param string Input string (UTF-8)
* @param boolean True to return list of words as array
* @param integer Minimum length of tokens
*
* @return mixed Normalized string or a list of normalized tokens
*/
public static function normalize_string($str, $as_array = false, $minlen = 2)
{
// replace 4-byte unicode characters with '?' character,
// these are not supported in default utf-8 charset on mysql,
// the chance we'd need them in searching is very low
$str = preg_replace('/('
. '\xF0[\x90-\xBF][\x80-\xBF]{2}'
. '|[\xF1-\xF3][\x80-\xBF]{3}'
. '|\xF4[\x80-\x8F][\x80-\xBF]{2}'
. ')/', '?', $str);
// split by words
$arr = self::tokenize_string($str, $minlen);
// detect character set
if (utf8_encode(utf8_decode($str)) == $str) {
// ISO-8859-1 (or ASCII)
preg_match_all('/./u', 'äâàåáãæçéêëèïîìíñöôòøõóüûùúýÿ', $keys);
preg_match_all('/./', 'aaaaaaaceeeeiiiinoooooouuuuyy', $values);
$mapping = array_combine($keys[0], $values[0]);
$mapping = array_merge($mapping, array('ß' => 'ss', 'ae' => 'a', 'oe' => 'o', 'ue' => 'u'));
}
else if (rcube_charset::convert(rcube_charset::convert($str, 'UTF-8', 'ISO-8859-2'), 'ISO-8859-2', 'UTF-8') == $str) {
// ISO-8859-2
preg_match_all('/./u', 'ąáâäćçčéęëěíîłľĺńňóôöŕřśšşťţůúűüźžżý', $keys);
preg_match_all('/./', 'aaaaccceeeeiilllnnooorrsssttuuuuzzzy', $values);
$mapping = array_combine($keys[0], $values[0]);
$mapping = array_merge($mapping, array('ß' => 'ss', 'ae' => 'a', 'oe' => 'o', 'ue' => 'u'));
}
foreach ($arr as $i => $part) {
$part = mb_strtolower($part);
if (!empty($mapping)) {
$part = strtr($part, $mapping);
}
$arr[$i] = $part;
}
return $as_array ? $arr : join(" ", $arr);
}
/**
* Compare two strings for matching words (order not relevant)
*
* @param string Haystack
* @param string Needle
*
* @return boolean True if match, False otherwise
*/
public static function words_match($haystack, $needle)
{
$a_needle = self::tokenize_string($needle, 1);
$_haystack = join(" ", self::tokenize_string($haystack, 1));
$valid = strlen($_haystack) > 0;
$hits = 0;
foreach ($a_needle as $w) {
if ($valid) {
if (stripos($_haystack, $w) !== false) {
$hits++;
}
}
else if (stripos($haystack, $w) !== false) {
$hits++;
}
}
return $hits >= count($a_needle);
}
/**
* Parse commandline arguments into a hash array
*
* @param array $aliases Argument alias names
*
* @return array Argument values hash
*/
public static function get_opt($aliases = array())
{
$args = array();
$bool = array();
// find boolean (no value) options
foreach ($aliases as $key => $alias) {
if ($pos = strpos($alias, ':')) {
$aliases[$key] = substr($alias, 0, $pos);
$bool[] = $key;
$bool[] = $aliases[$key];
}
}
for ($i=1; $i < count($_SERVER['argv']); $i++) {
$arg = $_SERVER['argv'][$i];
$value = true;
$key = null;
if ($arg[0] == '-') {
$key = preg_replace('/^-+/', '', $arg);
$sp = strpos($arg, '=');
if ($sp > 0) {
$key = substr($key, 0, $sp - 2);
$value = substr($arg, $sp+1);
}
else if (in_array($key, $bool)) {
$value = true;
}
else if (strlen($_SERVER['argv'][$i+1]) && $_SERVER['argv'][$i+1][0] != '-') {
$value = $_SERVER['argv'][++$i];
}
$args[$key] = is_string($value) ? preg_replace(array('/^["\']/', '/["\']$/'), '', $value) : $value;
}
else {
$args[] = $arg;
}
if ($alias = $aliases[$key]) {
$args[$alias] = $args[$key];
}
}
return $args;
}
/**
* Safe password prompt for command line
* from http://blogs.sitepoint.com/2009/05/01/interactive-cli-password-prompt-in-php/
*
* @return string Password
*/
public static function prompt_silent($prompt = "Password:")
{
if (preg_match('/^win/i', PHP_OS)) {
$vbscript = sys_get_temp_dir() . 'prompt_password.vbs';
$vbcontent = 'wscript.echo(InputBox("' . addslashes($prompt) . '", "", "password here"))';
file_put_contents($vbscript, $vbcontent);
$command = "cscript //nologo " . escapeshellarg($vbscript);
$password = rtrim(shell_exec($command));
unlink($vbscript);
return $password;
}
else {
$command = "/usr/bin/env bash -c 'echo OK'";
if (rtrim(shell_exec($command)) !== 'OK') {
echo $prompt;
$pass = trim(fgets(STDIN));
echo chr(8)."\r" . $prompt . str_repeat("*", strlen($pass))."\n";
return $pass;
}
$command = "/usr/bin/env bash -c 'read -s -p \"" . addslashes($prompt) . "\" mypassword && echo \$mypassword'";
$password = rtrim(shell_exec($command));
echo "\n";
return $password;
}
}
/**
* Find out if the string content means true or false
*
* @param string $str Input value
*
* @return boolean Boolean value
*/
public static function get_boolean($str)
{
$str = strtolower($str);
return !in_array($str, array('false', '0', 'no', 'off', 'nein', ''), true);
}
/**
* OS-dependent absolute path detection
*/
public static function is_absolute_path($path)
{
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
return (bool) preg_match('!^[a-z]:[\\\\/]!i', $path);
}
else {
return $path[0] == '/';
}
}
/**
* Resolve relative URL
*
* @param string $url Relative URL
*
* @return string Absolute URL
*/
public static function resolve_url($url)
{
// prepend protocol://hostname:port
if (!preg_match('|^https?://|', $url)) {
$schema = 'http';
$default_port = 80;
if (self::https_check()) {
$schema = 'https';
$default_port = 443;
}
$prefix = $schema . '://' . preg_replace('/:\d+$/', '', $_SERVER['HTTP_HOST']);
if ($_SERVER['SERVER_PORT'] != $default_port && $_SERVER['SERVER_PORT'] != 80) {
$prefix .= ':' . $_SERVER['SERVER_PORT'];
}
$url = $prefix . ($url[0] == '/' ? '' : '/') . $url;
}
return $url;
}
/**
* Generate a random string
*
* @param int $length String length
* @param bool $raw Return RAW data instead of ascii
*
* @return string The generated random string
*/
public static function random_bytes($length, $raw = false)
{
$hextab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$tabsize = strlen($hextab);
// Use PHP7 true random generator
if ($raw && function_exists('random_bytes')) {
return random_bytes($length);
}
if (!$raw && function_exists('random_int')) {
$result = '';
while ($length-- > 0) {
$result .= $hextab[random_int(0, $tabsize - 1)];
}
return $result;
}
$random = openssl_random_pseudo_bytes($length);
if ($random === false && $length > 0) {
throw new Exception("Failed to get random bytes");
}
if (!$raw) {
for ($x = 0; $x < $length; $x++) {
$random[$x] = $hextab[ord($random[$x]) % $tabsize];
}
}
return $random;
}
/**
* Convert binary data into readable form (containing a-zA-Z0-9 characters)
*
* @param string $input Binary input
*
* @return string Readable output (Base62)
* @deprecated since 1.3.1
*/
public static function bin2ascii($input)
{
$hextab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$result = '';
for ($x = 0; $x < strlen($input); $x++) {
$result .= $hextab[ord($input[$x]) % 62];
}
return $result;
}
/**
* Format current date according to specified format.
* This method supports microseconds (u).
*
* @param string $format Date format (default: 'd-M-Y H:i:s O')
*
* @return string Formatted date
*/
public static function date_format($format = null)
{
if (empty($format)) {
$format = 'd-M-Y H:i:s O';
}
if (strpos($format, 'u') !== false) {
$dt = number_format(microtime(true), 6, '.', '');
$dt .= '.' . date_default_timezone_get();
if ($date = date_create_from_format('U.u.e', $dt)) {
return $date->format($format);
}
}
return date($format);
}
/**
* Parses socket options and returns options for specified hostname.
*
* @param array &$options Configured socket options
* @param string $host Hostname
*/
public static function parse_socket_options(&$options, $host = null)
{
if (empty($host) || empty($options)) {
return $options;
}
// get rid of schema and port from the hostname
$host_url = parse_url($host);
if (isset($host_url['host'])) {
$host = $host_url['host'];
}
// find per-host options
if (array_key_exists($host, $options)) {
$options = $options[$host];
}
}
/**
* Get maximum upload size
*
* @return int Maximum size in bytes
*/
public static function max_upload_size()
{
// find max filesize value
$max_filesize = parse_bytes(ini_get('upload_max_filesize'));
$max_postsize = parse_bytes(ini_get('post_max_size'));
if ($max_postsize && $max_postsize < $max_filesize) {
$max_filesize = $max_postsize;
}
return $max_filesize;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Mar 19, 9:25 PM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458780
Default Alt Text
(376 KB)

Event Timeline