Page MenuHomePhorge

No OneTemporary

Size
260 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 59b0dc9e9..7cfb358f9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,1233 +1,1234 @@
CHANGELOG Roundcube Webmail
===========================
+- Deprecate $DB, $USER, $IMAP global variables, Use $RCMAIL instead
- Add option to set default font for HTML message (#1484137)
- Fix issues with big memory allocation of IMAP results
- Replace prompt() with jQuery UI dialog (#1485135)
- Fix navigation in messages search results
- Improved handling of some malformed values encoded with quoted-printable (#1488232)
- Add possibility to do LDAP bind before searching for bind DN
- Fix handling of empty <U> tags in HTML messages (#1488225)
- Add content filter for embedded attachments to protect from XSS on IE (#1487895)
- Use strpos() instead of strstr() when possible (#1488211)
- Fix handling HTML entities when converting HTML to text (#1488212)
- Fix fit_string_to_size() renders browser and ui unresponsive (#1488207)
- Fix handling of invalid characters in request (#1488124)
- Fix merging some configuration options in update.sh script (#1485864)
- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#1488208)
- Fix handling contact photo url with https:// prefix (#1488202)
- Prevent from memory_limit exceeding when trying to parse big messages bodies (#1487424)
- Add possibility to add SASL mechanisms for SMTP in smtp_connect hook (#1487937)
- Mark (with different color) folders with recent messages (#1486234)
- Fix possible infinite redirect on attachment preview (#1488199)
- Improved clickjacking protection for browsers which don't support X-Frame-Options headers
- Fixed bug where similiar folder names were highlighted wrong (#1487860)
- Fixed bug in handling link with '!' character in it (#1488195)
- Fixed bug where session ID's length was limited to 40 characters (#1488196)
- Added About tab in Settings
- TinyMCE:
- updated to 3.4.6
- security issue: removed moxieplayer (embedding flv and mp4 is not supported anymore)
RELEASE 0.7-beta
----------------
- Fix handling of HTML form elements in messages (#1485137)
- Fix regression in setting recipient to self when replying to a Sent message (#1487074)
- Fix listing of folders in hidden namespaces (#1486796)
- Don't consider \Noselect flag when building folders tree (#1488004)
- Fix sorting autocomplete results (#1488084)
- Add option to set session name (#1486433)
- Add option to skip alternative email addresses in autocompletion
- Fix inconsistent behaviour of Compose button in Drafts folder, add Edit button for drafts
- Fix problem with parsing HTML message body with non-unicode characters (#1487813)
- Add option to define matching method for addressbook search (#1486564, #1487907)
- Make email recipients separator configurable
- Fix so folders with \Noinferiors attribute aren't listed in parent selector
- Fix handling of curly brackets in URLs (#1488168)
- Fix handling of dates (birthday/anniversary) in contact data (#1488147)
- Fix error on opening searched LDAP contact (#1488144)
- Fix redundant line break in flowed format (#1488146)
- Fix IDN address validation issue (#1488137)
- Fix JS error when dst_active checkbox doesn't exist (#1488133)
- Autocomplete LDAP records when adding contacts from mail (#1488073)
- Plugin API: added 'ready' hook (#1488063)
- Ignore DSN request when it isn't supported by SMTP server (#1487800)
- Make sure LDAP name fields aren't arrays (#1488108)
- Fixed imap test to non-default port when using ssl (#1488118)
- Force all files to be overwritten when updating (#1488117)
- Fix issue where it wasn't possible to change list view mode in folder manager for INBOX (#1488107)
- Fix namespace handling in special folders settings (#1488112)
- Disable time limit for CLI scripts (#1488109)
- Fix misleading display when chaning editor type (#1488104)
- Add loading indicator on contact delete
- Fix bug where after delete message rows can be added to the list of another folder (#1487752)
- Add notice on autocompletion that not all records were displayed
- Add option 'searchonly' for LDAP address books
- Add Priority filter to the messages list
- Cache synchronization using QRESYNC/CONDSTORE
- Trigger 'new_messages' hook for all checked folders (#1488083)
- Make date/time format user configurable; drop 'date_today' config option
- Fix setting title for truncated subject in IE (#1487128)
- Fix displaying multipart/alternative messages with only one part (#1487938)
- Rewritten messages caching:
Indexes are stored in a separate table, so there's no need to store all messages in a folder
Added threads data caching
Flags are stored separately, so flag change doesn't cause DELETE+INSERT, just UPDATE
- Improved FETCH response handling
- Improvements in response tokenization method
- Use 'From' and 'To' labels instead of 'Sender' and 'Recipient'
- Fix username case-insensitivity issue in MySQL (#1488021)
- Addressbook Saved Searches
- Added spellchecker exceptions dictionary (shared or per-user)
- Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options)
- Added 'priority' column on messages list (#1486782)
- Localize forwarded message header (#1488058)
RELEASE 0.6
-----------
- Fix bug where the last identity is used on reply (#1488101)
- Fix locked folder rename option on servers supporting RFC2086 only (#1488089)
- Fix session race conditions when composing new messages
- Fix encoding of LDAP contacts identifiers (#1488079)
- jQuery 1.6.4
- Fix handling of binary attachments encoded with quoted-printable (#1488065)
- Fix text-overflow:ellipsis issues on messages list in FF7 and Webkit (#1488061)
- Fix handling of links with IP address
- Fix compacting folder resets message list filter (#1488076)
RELEASE 0.6-rc
----------------
- Send X-Frame-Options headers to protect from clickjacking (#1487037)
- Fallback to mail_domain in LDAP variable replacements; added 'host' to 'user_create' hook arguments (#1488024)
- Fixed wrong vCard type parameter mobile (#1488067)
- Fixed vCard WORKFAX issue (#1488046)
- Add vCard's Profile URL support (#1488062)
- jQuery 1.6.3
- Fix imap_cache setting to values other than 'db' (#1488060)
- Fix handling of attachments inside message/rfc822 parts (#1488026)
- Make list of mimetypes that open in preview window configurable (#1487625)
- Added plugin hook 'message_part_get' for attachment downloads
- Added unique connection identifier to IMAP debug messages
- Fix image type check for contact photo uploads
RELEASE 0.6-beta
----------------
- Fixed selecting identity on reply/forward (#1487981)
- Add option to hide selected LDAP addressbook on the list
- Add client-side checking of uploaded files size
- Add newlines between organization, department, jobtitle (#1488028)
- Recalculate date when replying to a message and localize the cite header (#1487675)
- Fix handling of email addresses with quoted local part (#1487939)
- Fix EOL character in vCard exports (#1487873)
- Added optional "multithreading" autocomplete feature
- Plugin API: Added 'config_get' hook
- Fixed new_user_identity plugin to work with updated rcube_ldap class (#1487994)
- Plugin API: added folder_delete and folder_rename hooks
- Added possibility to undo last contact delete operation
- Fix sorting of contact groups after group create (#1487747)
- Add optional textual upload progress indicator (#1486039)
- Fix parsing URLs containing commas (#1487970)
- Added vertical splitter for books/groups list in addressbook (#1487923)
- Improved namespace roots handling in folder manager
- Added searching in all addressbook sources
- Added addressbook source selection in contacts import
- Implement LDAPv3 Virtual List View (VLV) for paged results listing
- Use 'address_template' config option when adding a new address block (#1487944)
- Added addressbook advanced search
- Add popup with basic fields selection for addressbook search
- Case-insensitive matching in autocompletion (#1487933)
- Added option to force spellchecking before sending a message (#1485458)
- Fix handling of "<" character in contact data, search fields and folder names (#1487864)
- Fix saving "<" character in identity name and organization fields (#1487864)
- Added option to specify to which address book add new contacts
- Added plugin hook for keep-alive requests
- Store user preferences in session when write-master is not available and session is stored in memcache, write them later
- Improve performence of folder manager operations
- Fix default_port option handling in Installer when config.inc.php file exists (#1487925)
- Removed option focus_on_new_message, added newmail_notifier plugin
- Added general rcube_cache class with Memcache and APC support
- Improved caching performance by skipping writes of unchanged data
- Option enable_caching replaced by imap_cache and messages_cache options
- Fix WORKFAX saving in address book (#1487910)
- Add forward-as-attachment feature
- jQuery-1.6.2 (#1487913, #1487144)
- Improve display name composition when saving contacts (#1487143)
- Fix problems with subfolders of INBOX folder on some IMAP servers (#1487725)
- Fix handling of folders that doesn't belong to any namespace (#1487637)
- Enable multiselection for attachments uploading in capable browsers (#1485969)
- Add possibility to change HTML editor configuration by skin
- Fix a bug where selecting too many contacts would produce too large URI request (#1487892)
- Improve performance by including files with absolute path (#1487849)
- Move folder name truncation to client/skin (#1485412)
- Added plugin hook for request token creation
- Replace LDAP vars in group queries (#1487837)
- Fix vcard folding with uncode characters (#1487868)
- Keep all submitted data if contact form validation fails (#1487865)
- Handle uncode strings in rcube_addressbook::normalize_string() (#1487866)
- Fix handling of debug_level=4 in ajax requests (#1487831)
- Enable TinyMCE's contextmenu (#1487014)
- Allow multiple concurrent compose sessions
- New config option for custom logo
- Allow skins to define/override texts with <roundcube:label />
- Add simple ACL rights/namespace handling in folder manager
- Force IE to send referers (#1487806)
- Better display of vcard import results (#1485457)
- Improved vcard import
- Interactive update script with improved DB schema check
- Fix problem with contactgroupmembers table creation on MySQL 4.x, add index on contact_id column
- Add LDAP SASL bind and proxy authentication (#1486692)
- Replying to a sent message puts the old recipient as the new recipient (#1487074)
- Fulltext search over (almost) all data for contacts
- Extend address book with rich contact information
RELEASE 0.5.4
-------------
- Fix XSS vulnerability in UI messages (#1488030)
RELEASE 0.5.3
-------------
- Fix identities "reply-to" and "bcc" fields have a bogus value when left empty (#1487943)
- Fix issue which cases IMAP disconnection when encrypt() method was used (#1487900)
- Fix some CSS issues in Settings for Internet Explorer
- Fixed handling of folder with name "0" in folder selector
- Fix bug where messages were deleted instead moved to trash folder after Shift key was used (#1487902)
- Fix relative URLs handling according to a <base> in HTML (#1487889)
- Fix handling of top-level domains with more than 5 chars or unicode chars (#1487883)
- Fix usage of non-standard HTTP error codes (#1487797)
- Fix PHP warning on mistaken in_array() usage (#1487901)
RELEASE 0.5.2
-------------
- TinyMCE 3.4.2 now compatible with IE9
- PEAR::Net_SMTP 1.5.2, fixed timeout issue (#1487843)
- Fix bug where template name without plugin prefix was used in render_page hook
- Support 'abort' and 'result' response in 'preferences_save' hook, add error handling
- Fix bug where some content would cause hang on html2text conversion (#1487863)
- Improve space-stuffing handling in format=flowed messages (#1487861)
- Fix bug where some dates would produce SQL error in MySQL (#1487856)
- Added workaround for some IMAP server with broken STATUS response (#1487859)
- Fix bug where default_charset was not used for text messages (#1487836)
- Stateless request tokens. No keep-alive necessary on login page (#1487829)
- Force names of unique constraints in PostgreSQL DDL
- Add code for prevention from IMAP connection hangs when server closes socket unexpectedly
- Remove redundant DELETE query (for old session deletion) on login
- Get around unreliable rand() and mt_rand() in session ID generation (#1486281)
- Fix some emails are not shown using Cyrus IMAP (#1487820)
- Fix handling of mime-encoded words with non-integral number of octets in a word (#1487801)
- Fix parsing links with non-printable characters inside (#1487805)
- Fixed de_CH Localization bugs (#1487773)
- Add variable for 'Today' label in date_today option (#1486120)
- Fix dont_override setting does not override existing user preferences (#1487664)
- Use only one from IMAP authentication methods to prevent login delays (1487784)
- Support strftime format in date_today option
- Fix SQL query in rcube_user::query() so it uses index on MySQL again
- Removed redundant </form> tags from contact add/edit pages
- Fix CSS error in contact details screen on IE7 (#1487775)
RELEASE 0.5.1
-------------
- Fix handling of attachments with invalid content type (#1487767)
- Add workaround for DBMail's bug http://www.dbmail.org/mantis/view.php?id=881 (#1487766)
- Use IMAP's ID extension (RFC2971) to print more info into debug log
- Security: add optional referer check to prevent CSRF in GET requests
- Fix email_dns_check setting not used for identities/contacts (#1487740)
- Fix ICANN example addresses doesn't validate (#1487742)
- Security: protect login form submission from CSRF
- Security: prevent from relaying malicious requests through modcss.inc
- Fix handling of non-image attachments in multipart/related messages (#1487750)
- Fix IDNA support when IDN/INTL modules are in use (#1487742)
- Fix handling of invalid HTML comments in messages (#1487759)
- Fix parsing FETCH response for very long headers (#1487753)
- Fix add/remove columns in message list when message_sort_order isn't set (#1487751)
- Check mime headers before attempt to parse them (#1487745)
- Quote header values in show_additional_headers plugin (#1487744)
- Fix settings UI on IE 6 (#1487724)
- Remove double borders in folder listing (#1487713)
- Separate full message headers UI element from headers table (#1487715)
- Add part MIME ID to message_part_* hooks (#1487718)
- Improve parsing of MS Outlook vCards (#1487716)
- Updated PEAR::Net_Socket to 1.0.10
- Updated PEAR::Net_IDNA2 to 0.1.1
- Fix handling of comments inside an email address spec. (#1487673)
- Show full mail subject as title when hovering a cut subject link (#1487128)
- Fix randomly disappearing folders list in IE (#1487704)
- Fix list column add/removal in IE (#1487703)
- Fix login redirect issues (#1487686)
- Require PHP 5.2.1 or greater
- Fix %h/%z variables in username_domain option (#1487701)
- Workaround for setting charset in case of malformed bodystructure response (#1487700)
- Fix impossible to subscribe to protected folders (#1487656)
- Fix setting timezone in Preferences (#1487705)
RELEASE 0.5
-----------
- Fix double-login/session issue (#1487104)
- Wrap HTML parts with <html><body> and add Doctype declaration (#1487098)
- Make rcube_autoload silently skip unknown classes (#1487109)
- Fix charset detection in vcards with encoded values (#1485542)
- Better CSS cursors for splitters (#1486874)
- Show the same message only once (#1487641)
- Fix namespaces handling (#1487649)
- Add handling of multifolder METADATA/ANNOTATION responses
- Fix handling of INBOX when personal namespace prefix is non-empty (#1487657)
- Fix handling square brackets in links (#1487672)
- Add description of 'use_https' option in main.inc.php.dist file
RELEASE 0.5-RC
--------------
- Plugin API: Add 'pass' argument in 'authenticate' hook (#1487134)
- Fix attachments of type message/rfc822 are not listed on attachments list
- Add 'login_lc' config option for case-insensitive authentication (#1487113)
- Fix window is blur'ed in IE when selecting a message (#1487316)
- Fix cursor position on compose form in Webkit browsers (#1486674)
- Fix setting charset of attachment filenames (#1487122)
- Allow setting autocomplete attribute for all inputs separately (#1487313)
- New Folder Manager UI
- Fix invalid Request when creating a folder (#1487443)
- Add folder size and quota indicator in folder manager (#1485780)
- Add possibility to move a subfolder into root folder (#1486791)
- Fix copying all messages in a folder copies only messages from current page
- Improve performance of moving or copying of all messages in a folder
- Fix plaintext versions of HTML messages don't contain placeholders for emotions (#1485206)
- Improve performance of folder rename and delete actions
- Better support for READ-ONLY and NOPERM responses handling (#1487083)
- Add confirmation message on purge/expunge command response
- Fix handling of untagged responses for AUTHENTICATE command (#1487450)
- Add username and IP address to log message on unsuccessful login (#1487626)
- Improved Mail-Followup-To and Mail-Reply-To headers handling
- Fix charset conversion for text attachments without charset specification (#1487634)
RELEASE 0.5-BETA
----------------
- Make session data storage more robust against garbage session data (#1487136)
- Config option for autocomplete on login screen
- Allow plugin templates to include local files (#1487133)
- List groups in address detail view and allow to subscribe/unsubscribe from there (#1486753)
- Messages caching: performance improvements, fixed syncing, fixes related with #1486748
- Add link to identities in compose window (#1486729)
- Add Internationalized Domain Name (IDNA) support (#1483894)
- Add option to automatically send read notifications for known senders (#1485883)
- Add option to "Return receipt" will be always checked (#1486352)
- Fix HTML to plain text conversion doesn't handle citation blocks (#1486921)
- Use custom sorting when SORT is disabled by IMAP admin (#1486959)
- Allow setting some washtml options from plugin (#1486578)
- Add option do bind for an individual LDAP address book (#1486997)
- Change reply prefix to display email address only if sender name doesn't exist (#1486550)
- Plugin API: improved 'abort' flag handling, added 'result' item in some hooks (#1486914)
- Fix mailto optional params in plain text messages aren't handled (#1487026)
- Add Reply-to-List feature (#1484252)
- Add Mail-Followup-To/Mail-Reply-To support (#1485547)
- Fix confirmation message isn't displayed after sending mail on Chrome (#1486177)
- Fix keyboard doesn't work with autocomplete list with Chrome (#1487029)
- Improve tabs to fixed width and add tabs in identities info (#1486974)
- Add unique index on users.username+users.mail_host
- Make htmleditor option more consistent and add option to use HTML on reply to HTML message (#1485840)
- Use empty envelope sender address for message disposition notifications (RFC 2298.3)
- Support SMTP Delivery Status Notifications - RFC 3461 (#1486142)
- Use css sprite image for messages list
- Add (different) attachment icon for messages of type multipart/report (#1486165)
- Prevent from inserting empty link when composing HTML message (#1486944)
- Add caching support in id2uid and uid2id functions (#1487019)
- Add SASL proxy authentication for SMTP (#1486693)
- Improve displaying of UI messages (#1486977)
- Fix double e-mail filed in identity form (#1487054)
- Display IMAP errors for LIST/THREAD/SEARCH commands (#1486905)
- Add LITERAL+ (IMAP4 non-synchronizing literals) support (RFC 2088)
- Add separate column for message status icon (#1486665)
- Add ACL extension support into IMAP classes (RFC 4314)
- Add ANNOTATEMORE extension support into IMAP classes (draft-daboo-imap-annotatemore)
- Add METADATA extension support into IMAP classes (RFC 5464)
- Fix decoding of e-mail address strings in message headers (#1487068)
- Fix handling of attachments when Content-Disposition is not inline nor attachment (#1487051)
- Improve performance of unseen messages counting (#1487058)
- Improve performance of messages counting using ESEARCH extension (RFC4731)
- Add LIST-STATUS support in rcube_imap_generic class (RFC 5819)
- Add SASL-IR support in IMAP (RFC 4959)
- Add LOGINDISABLED support (RFC 2595)
- Add support for AUTH=PLAIN in IMAP authentication
- Re-implemented SMTP proxy authentication support
- Add support for IMAP proxy authentication (#1486690)
- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)
- Fix parent folder with unread subfolder not bold when message is open (#1487078)
- Add basic IMAP LIST's \Noselect option support
- Add support for selection options from LIST-EXTENDED extension (RFC 5258)
- Don't list subscribed but non-existent folders (#1486225)
- Fix handling of URLs with tilde (~) or semicolon (;) character (#1487087, #1487088)
- Plugin API: added 'contact_form' hook
- Add SORT=DISPLAY support (RFC 5957)
- Plugin API: add possibility to disable plugin in AJAX mode, 'noajax' property
- Plugin API: add possibility to disable plugin in framed mode, 'noframe' property
- Improve performance of setting IMAP flags using .SILENT suffix
- Improve performance of message cache status checking with skip_disabled=true
- Support contact's email addresses up to 255 characters long (#1487095)
- Add option to place replies in the folder of the message being replied to (#1485945)
- Add missing confirmation/error messages on contact/group/message actions (#1486845)
- Add 'loading' message on message move/copy/delete/mark actions
- Improve responsiveness of messages displaying (#1486986)
- Add option for minimum length of autocomplete's string (#1486428)
- Fix operations on messages in unsubscribed folder (#1487107)
- Add support for shared folders (#1403507)
- Fix handling of folders with name "0" (#1487119)
- Fix handling of folders with "<>" characters in name
- jQuery 1.4.4
- Fix handling of HTML entity strings in plain text messages
- Fix focused elements aren't unfocused when clicking on the list (#1487123)
- Fix error in MSSQL DDL scripts (#1487112)
- Lock submit button in onsubmit event on login page (#1487036)
- Don't set attachment's charset in Content-type header (#1487122)
- Fix handling of message bodies (quoted-printable encoded) with NULL characters (#1486189)
- Add workaround for MSOE's multipart/related messages with non-related attachments
RELEASE 0.4.2
-------------
- Fix handling of backslash as IMAP delimiter
- Fix charset replacement in HTML message bodies (#1487021)
- Fix: contact group input is empty when using rename action more than once on the same group record
- Fix "Server Error! (Not Found)" when using utils/save-pref action (#1487023)
- Fix handling of Thunderbird's vCards (#1487024)
RELEASE 0.4.1
-------------
- Fix space-stuffing in format=flowed messages (#1487018)
- Fix msgexport.sh now using the new imap wrapper
- Avoid displaying password on shell (#1486947)
- Only lower-case user name if first login attempt failed (#1486393)
- Make alias setting in squirrelmail_usercopy plugin configurable (patch by pommi, #1487007)
- Prevent from saving a non-existing skin path in user prefs (#1486936)
- Improve handling of single-part messages with bogus BODYSTRUCTURE (#1486898)
- Fix path to SQL files when using pgsql/mysqli/sqlsrv drivers (#1486902)
- Fix upgrade script for SQLite (#1486903)
- Fixes in SQL init script + added update script for MSSQL database
- Remove redundant date in syslog messages (#1486945)
- Fix contacts list page controls when a group is selected (#1486946)
- Fix SMTP test in Installer (#1486952)
- Fix "Select all" causes message to be opened in folder with exactly one message (#1486913)
- Fix Tab key doesn't work in HTML editor in Google Chrome (#1486925)
- Fix TinyMCE uses zh_CN when zh_TW locale is set (#1486929)
- Fix TinyMCE buttons are hidden in Opera (#1486922)
- Fix JS error on IE when trying to send HTML message with enabled spellchecker (#1486940)
- Display inline images with known extensions and non-image content-type (#1486934)
- Fix "Threaded" checkbox after subfolder creation (#1486928)
- Fix timezone string in sent mail (#1486961)
- Show disabled checkboxes for protected folders instead of dots (#1485498)
- Added fieldsets in Identity form, added 'identity_form' hook
- Re-added 'Close' button in upload form (#1486930, #1486823)
- Fix handling of charsets with LATIN-* label
- Fix messages background image handling in some cases (#1486990)
- Fix format=flowed handling (#1486989)
- Fix when IMAP connection fails in 'get' action session shouldn't be destroyed (#1486995)
- Fix list_cols is not updated after column dragging (#1486999)
- Support %z variable in host configuration options (#1487003)
RELEASE 0.4
-----------
- Fix disapearing upload form disapears when user selects a file on Safari (#1486823)
- Don't replace error messages with loading info (#1486300)
- Fix JS errors on compose mode switch (#1486870)
- Fix message structure parsing when it lacks optional fields (#1486881)
- Include all recipients in sendmail log
- Support HTTP_X_FORWARDED_PROTO header for HTTPS detecting (#1486866)
- Fix default IMAP port configuration (#1486864)
- Create Sent folder when starting to compose a new message (#1486802)
- Fix handling of messages with Content-Type: application/* and no filename (#1484050)
- Improved compose screen: resizable body and attachments list, vertical splitter, options menu
- Fix RC forgets search results (#1483883)
- TinyMCE 3.3.7
- Improve parsing of styled empty tags in HTML messages (#1486812)
- Add %dc variable support in base_dn/bind_dn config (#1486779)
- Add button to hide/unhide the preview pane (#1484215)
- Fix no-cache headers on https to prevent content caching by proxies (#1486798)
- Fix attachment filenames broken with TNEF decoder using long filenames (#1486795)
- Use user's timezone in Date header, not server's timezone (#1486119)
- Add option to set separate footer for HTML messages (#1486660)
- Add real SMTP error description to displayed error messages (#1485927)
- Fix some IMAP errors handling when opening the message (#1485443)
- Fix related parts aren't displayed when got mimetype other than image/* (#1486432)
- Multiple identity and database support for squirrelmail_usercopy plugin (#1486517)
- Support dynamic hostname (%d/%n) variables in configuration options (#1485438)
- Add 'messages_list' hook (#1486266)
- Add request* event triggers in http_post/http_request (#1486054)
- Fix use RFC-compliant line-delimiter when saving messages on IMAP (#1486712)
- Add 'imap_timeout' option (#1486760)
- Fix forwarding of messages with winmail attachments
- Fix handling of uuencoded attachments in message body (#1485839)
- Added list_mailboxes hook in rcube_imap::list_unsubscribed() (#1486668)
- Fix wrong message on file upload error (#1486725)
- Add support for data URI scheme [RFC2397] (#1486740)
- Added 'actionbefore', 'actionafter', 'responsebefore', 'responseafter' events
- Fix double-addition of e-mail domain to content ID in HTML images
- Read and send messages with format=flowed (#1484370), fixes word wrapping issues (#1486543)
- Fix duplicated attachments when forwarding a message (#1486487)
- Fix message/rfc822 attachments containing only attachments are not parsed properly (#1486743)
- Fix %00 character in winmail.dat attachments names (#1486738)
- Fix handling errors of folder deletion (#1486705)
- Parse untagged CAPABILITY response for LOGIN command (#1486742)
- Renamed all php-cli scripts to use .sh extension
- Some files from /bin + spellchecking actions moved to the new 'utils' task
- Added thread tree icons
- Extend contact groups support (#1486682)
- Fix check-recent action issues and performance (#1486526)
- Fix messages order after checking for recent (#1484664)
- Fix autocomplete shows entries without email (#1486452)
- Fix listupdate event doesn't trigger on search response (#1486708)
- Fix select_all_mode value after selecting a message (#1486720)
- Set focus to editor on reply in HTML mode (#1486632)
- Fix composing in HTML jumps cursor to body instead of recipients (#1486674)
- Allow columns order change per user - drag&drop (#1485795)
- Add References header in read receipt (#1486681)
- Fix database constraint violation when opening a message (#1486696)
- Add 'loading' message while login is in progress (#1486667)
- Fix quota_zero_as_unlimited (#1486662)
- Fix folder subscription checking (#1486684)
- Fix INBOX appears (sometimes) twice in mailbox list (#1486672)
- Fix listing of attachments of some types e.g. "x-epoc/x-sisx-app" (#1486653)
- Fix DB Schema checking when some db_table_* options are not set (#1486654)
RELEASE 0.4-beta
----------------
- Add sizelimit and timelimit variables in LDAP config (#1486544)
- Hide IMAP host dropdown when single host is defined (#1486326)
- Add images pre-loading on login page (#1451160)
- Add HTTP_X_REAL_IP and HTTP_X_FORWARDED_FOR to successful logins log (#1486441)
- Fix setting spellcheck languages with extended codes (#1486605)
- Fix messages list scrolling in FF3.6 (#1486472)
- Fix quicksearch input focus (#1486637)
- Always set changed date when flagging a DB record as deleted + provide a cleanup script
- Fix address book/group selection (#1486619)
- Assign newly created contacts to the active group (#1486626)
- Added option not to mark messages as read when viewed in preview pane (#1485012)
- Allow plugins modify the Sent folder when composing (#1486548)
- Added optional (max_recipients) support to restrict total number of recipients per message (#1484542)
- Re-organize editor buttons, add blockquote and search buttons
- Make possible to write inside or after a quoted html message (#1485476)
- Fix bugs on unexpected IMAP connection close (#1486190, #1486270)
- Iloha's imap.inc rewritten into rcube_imap_generic class
- Added contact groups in address book (not finished yet)
- Added PageUp/PageDown/Home/End keys support on lists (#1486430)
- Added possibility to select all messages in a folder (#1484756)
- Added 'imap_force_caps' option for after-login CAPABILITY checking (#1485750)
- Password: Support dovecotpw encryption
- TinyMCE 3.3.1
- Implemented messages copying using drag&drop + SHIFT (#1484086)
- Improved performance of folders operations (#1486525)
- Fix blocked.gif attachment is not attached to the message (#1486516)
- Managesieve: import from Horde-INGO
- Managesieve: support for more than one match (#1486078)
- Managesieve: support for selectively disabling rules within a single sieve script (#1485882)
- Threaded message listing now available
- Added sorting by ARRIVAL and CC
- Message list columns configurable by the user
- Removed 'index_sort' option, now we're using empty 'message_sort_col' for this
- virtuser_query: support other identity data (#1486148)
- Options virtuser_* replaced with virtuser_* plugins
- Plugin API: Implemented 'email2user' and 'user2email' hooks
- Fix forwarding message omits CC header (#1486305)
- Add 'default_charset' option to user preferences (#1485451)
- Add 'delete_always' option to user preferences
- Support/Require tls:// prefix in 'smtp_server' option for TLS connections
- Fix inconsistent behaviour of 'delete_always' option (#1486299)
- Fix deleting all messages from last list page (#1486293)
- Flag original messages when sending a draft (#1486203)
- Changed signature separator when top-posting (#1486330)
- Let the admin define defaults for search modifiers (#1485897)
- Fix long e-mail addresses validation (#1486453)
- Remember search modifiers in user prefs (#1486146)
- Added force_7bit option to force MIME encoding of plain/text messages (#1486510)
- Use case sensitive check when checking for default folders (#1486346)
- Fix checking for new mail: now checks unseen count of inbox (#1485794)
- Improve performance by avoiding unnecessary updates to the session table (#1486325)
- Fix invalid <font> tags which cause HTML message rendering problems (#1486521)
- Fix CVE-2010-0464: Disable DNS prefetching (#1486449)
- Fix Received headers to behave better with SpamAssassin (#1486513)
- Password: Make passwords encoding consistent with core, add 'password_charset' global option (#1486473)
- Fix adding contacts SQL error on mysql (#1486459)
- Squirrelmail_usercopy: support reply-to field (#1486506)
- Fix IE spellcheck suggestion popup issue (#1486471)
- Fix email address auto-completion shows regexp pattern (#1486258)
- Fix merging of configuration parameters: user prefs always survive (#1486368)
- Fix quota indicator value after folder purge/expunge (#1486488)
- Fix external mailto links support for use as protocol handler (#1486037)
- Fix attachment excessive memory use, support messages of any size (#1484660)
- Fix setting task name according to auth state
- Password: fix vpopmaild driver (#1486478)
- Add workaround for MySQL bug [http://bugs.mysql.com/bug.php?id=46293] (#1486474)
- Fix quoted text wrapping when replying to an HTML email in plain text (#1484141)
- Fix handling of extended mailto links (with params) (#1486354)
- Fix sorting by date of messages without date header on servers without SORT (#1486286)
- Fix inconsistency when not using default table names (#1486467)
- Fix folder rename/delete buttons do not appear on creation of first folder (#1486468)
- Fix character set conversion fails on systems where iconv doesn't accept //IGNORE (#1486375)
- Log in performance: Create default folders on first login only
- Import contacts into the selected address book (by Phil Weir)
- Add support for MDB2's 'sqlsrv' driver (#1486395)
- Use jQuery-1.4
- Removed problematic browser-caching of messages
- Fix incompatybility with suhosin.executor.disable_emodifier (#1486321)
- Use PLAIN auth when CRAM fails and imap_auth_type='check' (#1486371)
- Fix removal of <title> tag from HTML messages (#1486432)
- Fix 'force_https' to specified port when URL contains a port number (#1486411)
- Fix to-text converting of HTML entities inside b/strong/th/hX tags (#1486422)
- Bug in spellchecker suggestions when server charset != UTF8 (#1486406)
- Managesieve: Fix requires generation for multiple actions (#1486397)
- Fix LDAP problem with special characters in RDN (#1486320)
- Improved handling of message parts of type message/rfc822
- Plugin API: added 'quota' hook
- Fix parsing conditional comments in HTML messages (#1486350)
- Use built-in json_encode() for proper JSON format in AJAX replies
- Allow setting only selected params in 'message_compose' hook (#1486312)
- Plugin API: added 'message_compose_body' hook (#1486285)
- Fix counters of all folders are checked in 'getunread' action with check_all_folders disabled (#1486128)
- Fix displaying alternative parts in messages of type message/rfc822 (#1486246)
- Fix possible messages exposure when using Roundcube behind a proxy (#1486281)
- Fix unicode para and line separators in javascript response (#1486310)
- Additional_message_headers: allow unsetting headers, support plugin's config file (#1486268)
- Fix displaying of hidden directories in skins list (#1486301)
- Fix open_basedir restriction error when reading skins list (#1486304)
- Fix pasting from Office apps into html editor (#1486271)
- Fix empty <a> tags parsing (#1486272)
- Don't cut off attachment names when using non-RFC2231 encoding (#1485515)
- Allow inserting signatures above replied message body (#1484272)
- Managesieve 2.0: multi-script support
- Fix imap_auth_type regression (#1486263)
RELEASE 0.3.1
------------------
- Specify toolbar container in compose template (#1486247)
- Fix $_SERVER['HTTPS'] check for SSL forcing on IIS (#1486243)
- Avoid unnecessary page loads for selected tab (#1486032)
- Fix quota indicator issues by content generation on client-size (#1486197, #1486220)
- Don't display disabled sections in Settings (#1486099)
- Added server-side e-mail address validation with 'email_dns_check' option (#1485857)
- Fix login page loading into an iframe when session expires (#1485952)
- Allow setting port number in 'force_https' option (#1486091)
- Option 'force_https' replaced by 'force_https' plugin
- Fix IE issue with non-UTF-8 characters in AJAX response (#1486159)
- Partially fixed "empty body" issue by showing raw body of malformed message (#1486166)
- Fix importing/sending to email address with whitespace (#1486214)
- Added XIMSS (CommuniGate) driver for Password plugin
- Fix newly attached files are not saved in drafts w/o editing any text (#1486202)
- Added attachment upload indicator with parallel upload (#1486058)
- Use default_charset for bodies of messages without charset definition (#1486187)
- Password: added cPanel driver
- Fix return to first page from e-mail screen (#1486105)
- Fix handling HTML comments in HTML messages (#1486189)
- Fix folder/messagelist controls alignment - icons used (#1486072)
- Fix LDAP addressbook shows 'Contact not found' error sometimes (#1486178)
- Fix cache status checking + improve cache operations performance (#1486104)
- Prevent from setting INBOX as any of special folders (#1486114)
- Fix regular expression for e-mail address (#1486152)
- Fix Received header format
- Implemented sorting by message index - added 'index_sort' option (#1485936)
- Fix dl() use in installer (#1486150)
- Added 'ldap_debug' option
- Fix "Empty startup greeting" bug (#1486085)
- Fix setting user name in 'new_user_identity' plugin (#1486137)
- Fix incorrect count of new messages in folder list when using multiple IMAP clients (#1485995)
- Fix all folders checking for new messages with disabled caching (#1486128)
- Support skins in 'archive' and 'markasjunk' plugins
- Added 'html_editor' hook (#1486068)
- Fix DB constraint violation when populating messages cache (#1486052)
- Password: added password strength options (#1486062)
- Fix LDAP partial result warning (#1485536)
- Fix delete in message view deletes permanently with flag_for_deletion=true (#1486101)
- Use faster/secure mt_rand() (#1486094)
- Fix roundcube hangs on empty inbox with bincimapd (#1486093)
- Fix wrong headers for IE on servers without $_SERVER['HTTPS'] (#1485926)
- Force IE style headers for attachments in non-HTTPS session, 'use_https' option (#1485655)
- Check 'post_max_size' for upload max filesize (#1486089)
- Password Plugin: Fix %d inserts username instead of domain (#1486088)
- Fix rcube_mdb2::affected_rows() (#1486082)
RELEASE 0.3-stable
------------------
- Fix gn and givenName should be synonymous in LDAP addressbook (#1485892)
- Add mail_domain to LDAP email entries without @ sign (#1485201)
- Fix saving empty values in LDAP contact data (#1485781)
- Fix LDAP contact update when RDN field is changed (#1485788)
- Fix LDAP attributes case senitivity problems (#1485830)
- Fix LDAP addressbook browsing when only one directory is used (#1486022)
- Fix endless loop on error response for APPEND command (#1486060)
- Don't require date.timezone setting in installer (#1485989)
- Fix date sorting problem with Courier IMAP server (#1486065)
- Unselect pressed buttons on mouse up (#1485987)
- Don't set php_value error_log in .htaccess but mention in INSTALL (#1485924)
- Fix too small status/flag/attachment columns in Safari 4 (#1486063)
- Fix selection disabling while dragging splitter in webkit browsers (#1486056)
- Added 'new_messages' plugin hook (#1486005)
- Added 'logout_after' plugin hook (#1486042)
- Added 'message_compose' hook
- Added 'imap_connect' hook (#1485956)
- Fix vcard_attachments plugin (#1486035)
- Updated PEAR::Auth_SASL to 1.0.3 version
- Use sequence names only with PostgreSQL (#1486018)
- Re-designed User Preferences interface
- Fix MS SQL DDL (#1486020)
- Fix rcube_mdb2.php: call to setCharset not implemented in mssql driver (#1486019)
- Added 'display_next' option
- Fix rcube_mdb2::unixtimestamp for MS SQL (#1486015)
- Fix HTML washing to respect character encoding
- Fix endless loop in iil_C_Login() with Courier IMAP (#1486010)
- Fix #messagemenu display on IE (#1486006)
- Speedup UI by using sprites for (toolbar) buttons
- Fix charset names with X- prefix handling
- Fix displaying of HTML messages with unknown/malformed tags (#1486003)
RELEASE 0.3-RC1
---------------
- Fix import of vCard entries with params (#1485453)
- Fix HTML messages output with empty block elements (#1485974)
- Use request tokens to protect POST requests from CSRF
- Added hook when killing a session
- Added hook to write_log function (#1485971)
- Performance improvements by use UID commands (#1485690)
- Fix HTML editor tabIndex setting (#1485972)
- Added 'imap_debug' and 'smtp_debug' options
- Support strftime's format modifiers in date_* options (#1484806)
- Support %h variable in 'smtp_server' option (#1485766)
- Show SMTP errors in browser (#1485927)
- Allow WBR tag in HTML message (#1485960)
- Use spl_autoload_register() instead of __autoload (#1485947)
- Add hook for identities listing (#1485958)
- Trigger hook 'smtp_connect' when opening an SMTP connection (#1485954)
- Added config option to enforce HTTPS connections
- Fix non-unicode characters caching in unicode database (#1484608)
- Performance improvements of messages caching
- Fix empty Date header issue (#1485923)
- Open collapsed folders during drag & drop (#1485914)
- Fixed link text replacements (#1485789)
- Also trigger 'insertrow' events on page load (#1485826)
- No link on subject in IE browsers (#1484913)
- Fixed filename encoding according to RFC2231 (#1485875)
- Added message Edit feature (#1483891, #1484440)
- Fix message Etag generation for counter issues (#1485623)
- Fix messages searching on MailEnable IMAP (#1485762)
- Fixed many 'skip_deleted' issues (#1485634)
- Fixed messages list sorting on servers without SORT capability
- Colorized signatures in plain text messages
- Reviewed/fixed skip_deleted/read_when_deleted/flag_for_deletion options handling in UI
- Fix displaying of big maximum upload filesize (#1485889)
- Added possibility to invert messages selection
- After move/delete from 'show' action display next message instead of messages list (#1485887)
- Fixed problem with double quote at the end of folder name (#1485884)
- Speedup UI by using CSS sprites and etags/expires/deflate in Apache config (#1484858,#1485800)
- Support UID EXPUNGE: remove only moved/deleted messages
- Add drag cancelling with ESC key (#1484344)
- Support initial identity name from virtuser_query (#1484003)
- Added message menu, removed Print and Source buttons
- Added possibility to save message as .eml file (#1485861)
- Added 1 minute interval in autosave options (#1485854)
- Support UTF-7 encoding in messages (#1485832)
- Better support for malformed character names (#1485758)
RELEASE 0.3-BETA
----------------
- Plugin API + jQuery engine
- Added possibility to encrypt received header, option 'http_received_header_encrypt',
added some more logic in encrypt/decrypt functions for security
- Fix Answered/Forwarded flag setting for messages in subfolders
- Fix autocomplete problem with capital letters (#1485792)
- Support UUencode content encoding (#1485839)
- Minimize chance of race condition in session handling (#1485659, #1484678)
- Fix session handling on non-session SQL query error (#1485734)
- Fix html editor mode setting when reopening draft message (#1485834)
- Added quick search box menu (#1484304)
- Fix wrong column sort order icons (#1485823)
- Updated TinyMCE to 3.2.3 version
- Fix attachment names encoding when charset isn't specified in attachment part (#1484969)
- Fix message normal priority problem (#1485820)
- Fix autocomplete spinning wheel does not disappear (#1485804)
- Added log_date_format option (#1485709)
- Fix text wrapping in HTML editor after switching from plain text to HTML (#1485521)
- Fix auto-complete function hangs with plus sign (#1485815)
- Fix AJAX requests errors handler (#1485000)
- Speed up message list displaying on IE
- Fix read/write database recognition (#1485811)
RELEASE 0.2.2
-------------
- Fix quicksearchbox look in Chrome and Konqueror (#1484841)
- Fix UTF-8 byte-order mark removing (#1485514)
- Fix folders subscribtions on Konqueror (#1484841)
- Fix debug console on Konqueror and Safari
- Fix messagelist focus issue when modifying status of selected messages (#1485807)
- Support STARTTLS in IMAP connection (#1485284)
- Fix DEL key problem in search boxes (#1485528)
- Support several e-mail addresses per user from virtuser_file (#1485678)
- Fix drag&drop with scrolling on IE (#1485786)
- Fix adding signature separator in html mode (#1485350)
- Fix opening attachment marks message as read (#1485803)
- Fix 'temp_dir' does not support relative path under Windows (#1484529)
- Fix "Initialize Database" button missing from installer (#1485802)
- Fix compose window doesn't fit 1024x768 window (#1485396)
- Fix service not available error when pressing back from compose dialog (#1485552)
- Fix using mail() on Windows (#1485779)
- Fix word wrapping in message-part's <PRE>s for printing (#1485787)
- Fix incorrect word wrapping in outgoing plaintext multibyte messages (#1485714)
- Fix double footer in HTML message with embedded images
- Fix TNEF implementation bug (#1485773)
- Fix incorrect row id parsing for LDAP contacts list (#1485784)
- Fix 'mode' parameter in sqlite DSN (#1485772)
RELEASE 0.2.1
------------------
- Use US-ASCII as failover when Unicode searching fails (#1485762)
- Fix errors handling in IMAP command continuations (#1485762)
- Fix FETCH result parsing for servers returning flags at the end of result (#1485763)
- Fix datetime columns defaults in mysql's DDL (#1485641)
- Fix attaching more than nine inline images (#1485759)
- Support 'UNICODE-1-1-UTF-7' alias for UTF-7 encoding (#1485758)
- Fix mime-type detection using a hard-coded map (#1485311)
- Don't return empty string if charset conversion failed (#1485757)
- Disable concurrent autocomplete query results display (#1485743)
- Fix new lines stripped from message footer (#1485751)
- Fix IE problem with mouse click autocomplete (#1485739)
- Fix html body washing on reply/forward + fix attachments handling (#1485676)
- Fix multiple recipients input parsing (#1485733)
- Fix replying to message with html attachment (#1485676)
- Use default_charset for messages without specified charset (#1485661, #1484961)
- Support non-standard "GMT-XXXX" literal in date header (#1485729)
- Added TNEF support to decode MS Outlook attachments (winmail.dat)
- Fix "value continuation" MIME headers by adding required semicolon (#1485727)
- Fix pressing select all/unread multiple times (#1485723)
- Fix selecting all unread does not honor new messages (#1485724)
- Fix some base64 encoded attachments handling (#1485725)
- Support NGINX as IMAP backend: better BAD response handling (#1485720)
- Performance fix: don't fetch attachment parts headers twice to parse filename
- Fix checking for recent messages on various IMAP servers (#1485702)
- Performance fix: Don't fetch quota and recent messages in "message view" mode
- Fix displaying of alternative-inside-alternative messages (#1485713)
- Fix MDNSent flag checking, use arbitrary keywords (asterisk) flag (#1485706)
- Fix creation of folders with '&' sign in name
- Fix parsing of email addresses without angle brackets (#1485693)
- Save spellcheck corrections when switching from plain to html editor (and spellchecking is on)
- Fix large search results on server without SORT capability (#1485668)
- Get rid of preg_replace() with eval modifier and create_function usage (#1485686)
- Bring back <base> and <link> tags in HTML messages
- Fix XSS vulnerability through background attributes as reported by Julien Cayssol
- Fix problems with backslash as IMAP hierarchy delimiter (#1484467)
- Secure vcard export by getting rid of preg's 'e' modifier use (#1485689)
- Fix authentication when submitting form with existing session (#1485679)
- Allow absolute URLs to images in HTML messages/sigs (#1485666)
- Fix message body which contains both inline attachments and emotions
- Fix SQL query execution errors handling in rcube_mdb2 class (#1485509)
- Fix address names with '@' sign handling (#1485654)
- Improve messages display performance
- Fix messages searching with 'to:' modifier
RELEASE 0.2-STABLE
------------------
- Fix mark popup in IE 7 (#1485369)
- Fix line-break issue when copy & paste in Firefox (#1485425)
- Fix autocomplete "unknown server error" (#1485637)
- Fix STARTTLS before AUTH in SMTP connection (#1484883)
- Support multiple quota values in QUOTAROOT resonse (#1485626)
- Only abbreviate file name for IE < 7 browsers (#1485063)
- Performance: allow setting imap rootdir and delimiter before connect (#1485172)
- Fix sorting of folders with more than 2 levels (#1485569)
- Fix search results page jumps in LDAP addressbook (#1485253)
- Fix empty line before the signature in IE (#1485351)
- Fix horizontal scrollbar in preview pane on IE (#1484633)
- Add Robots meta tag in login page and installer (#1484846)
- Added 'show_images' option, removed 'addrbook_show_images' (#1485597)
- Option to check for new mails in all folders (#1484374)
- Don't set client busy when checking for new messages (#1485276)
- Allow UTF-8 folder names in config (#1485579)
- Add junk_mbox option configuration in installer (#1485579)
- Do serverside addressbook queries for autocompletion (#1485531)
- Allow setting attachment col position in 'list_cols' option
- Allow override 'list_cols' via skin (#1485577)
- Fix 'cache' table cleanup on session destroy (#1485516)
- Increase speed of session destroy and garbage clean up
- Fix session timeout when DB server got clock skew (#1485490)
- Fix handling of some malformed messages (#1484438)
- Speed up raw message body handling
- Better HTML entities conversion in html2text (#1485519)
- Fix big memory consumption and speed up searching on servers without SORT capability
- Fix setting locale to tr_TR, ku and az_AZ (#1485470)
- Use SORT for searching on servers with SORT capability
- Added message status filter
- Fix empty file sending (#1485389)
- Improved searching with many criterias (calling one SEARCH command)
- Fix HTML editor initialization on IE (#1485304)
- Add warning when switching editor mode from html to plain (#1485488)
- Make identities list scrollable (#1485538)
- Fix problem with numeric folder names (#1485527)
- Added BYE response simple support to prevent from endless loops in imap.inc (#1483956)
- Fix unread message unintentionally marked as read if read_when_deleted=true (#1485409)
- Remove port number from SERVER_NAME in smtp_helo_host (#1485518)
- Don't send disposition notification receipts for messages marked as 'read' (#1485523)
- Added 'keep_alive' and 'min_keep_alive' options (#1485360)
- Added option 'identities_level', removed 'multiple_identities'
- Allow deleting identities when multiple_identities=false (#1485435)
- Added option focus_on_new_message (#1485374)
- Fix html2text class autoloading on Windows (#1485505)
- Fix html signature formatting when identity save error occured (#1485426)
- Add feedback and set busy when moving folder (#1485497)
- Fix 'Empty' link visibility for some languages e.g. Slovak (#1485489)
- Fix messages count bar overlapping (#1485270)
- Fix adding signature in drafts compose mode (#1485484)
- Fix iil_C_Sort() to support very long and/or divided responses (#1485283)
- Fix matching case sensitivity when setting identity on reply (#1485480)
- Prefer default identity on reply
- Fix imap searching on ISMail server (#1485466)
- Add css class for flagged messages (#1485464)
- Write username instead of id in sendmail log (#1485477)
- Fix htmlspecialchars() use for PHP version < 5.2.3 (#1485475)
- Fix js keywords escaping in json_serialize() for IE/Opera (#1485472)
- Added bin/killcache.php script (#1485434)
- Add support for SJIS, GB2312, BIG5 in rc_detect_encoding()
- Fix vCard file encoding detection for non-UTF-8 strings (#1485410)
- Add 'skip_deleted' option in User Preferences (#1485445)
- Minimize "inline" javascript scripts use (#1485433)
- Fix css class setting for folders with names matching defined classes names (#1485355)
- Fix race conditions when changing mailbox
- Fix spellchecking when switching to html editor (#1485362)
- Fix compose window width/height (#1485396)
- Allow calling msgimport.sh/msgexport.sh from any directory (#1485431)
- Localized filesize units (#1485340)
- Better handling of "no identity" and "no email in identity" situations (#1485117)
- Added 'mime_param_folding' option with possibility to choose long/non-ascii attachment names encoding eg. to be readable in MS Outlook/OE (#1485320)
- Added "advanced options" feature in User Preferences
- Fix unread counter when displaying cached massage in preview panel (#1485290)
- Fix htmleditor spellchecking on MS Windows (#1485397)
- Fix problem with non-ascii attachment names in Mail_mime (#1485267, #1485096)
- Fix language autodetection (#1485401)
- Fix button label in folders management (#1485405)
- Fix collapsed folder not indicating unread msgs count of all subfolders (#1485403)
- Fix handling of apostrophes in filenames decoded according to rfc2231
RELEASE 0.2-BETA
----------------
- Made config files location configurable (#1485215)
- Reduced memory footprint when forwarding attachments (#1485345)
- Allow and use spellcheck attribute for input/textarea fields (#1485060)
- Added icons for forwarded/forwarded+replied messages (#1485257)
- Added Reply-To to forwarded emails (#1485315)
- Display progress message for folders create/delete/rename (#1485357)
- Smart Tags and NOBR tag support in html messages (#1485363, #1485327)
- Redesign of the identities settings (#1484042)
- Add config option to disable creation/deletion of identities (#1484498)
- Added 'sendmail_delay' option to restrict messages sending interval (#1484491)
- Added vertical splitter for folders list resizing
- Added possibility to view all headers in message view
- Fixed splitter drag/resize on Opera (#1485170)
- Fixed quota img height/width setting from template (#1484857)
- Refactor drag & drop functionality. Don't rely on browser events anymore (#1484453)
- Insert "virtual" folders in subscription list (#1484779)
- Added link to open message in new window
- Enable export of address book contacts as vCard
- Add feature to import contacts from vcard files (#1326103)
- Respect Content-Location headers in multipart/related messages according to RFC2110 (#1484946)
- Allowed max. attachment size now indicated in compose screen (#1485030)
- Also capture backspace key in list mode (#1484566)
- Allow application/pgp parts to be displayed (#1484753)
- Correctly handle options in mailto-links (#1485228)
- Immediately save sort_col/sort_order in user prefs (#1485265)
- Truncate very long (above 50 characters) attachment filenames when displaying
- Allow to auto-detect client language if none set (#1484434)
- Auto-detect the client timezone (user configurable)
- Add RFC2231 header value continuations support for attachment filenames + hack for servers that not support that feature
- Fix Reply-To header displaying (#1485314)
- Mark form buttons that provide the most obvious operation (mainaction)
- Added option 'quota_zero_as_unlimited' (#1484604)
- Added PRE handling in html2text class (#1484740)
- Added folder hierarchy collapsing
- Added options to use syslog instead of log file (#1484850)
- Added Logging & Debugging section in Installer
- Fix In-Reply-To and References headers when composing saved draft message (#1485288)
- Fix html message charset conversion for charsets with underline (#1485287)
- Fix buttons status after contacts deletion (#1485233)
- Fix escaping of To: and From: fields when building message body for reply or forward in the HTML editor (#1484904)
- Use current mailbox name in template (#1485256)
- Better fix for skipping untagged responses (#1485261)
- Added pspell support patch by Kris Steinhoff (#1483960)
- Enable spellchecker for HTML editor (#1485114)
- Respect spellcheck_uri in tinyMCE spellchecker (#1484196)
- Case insensitive contacts searching using PostgreSQL (#1485259)
- Make default imap folders configurable for each user (#1485075)
- Save outgoing mail to selectable folder (#1324581)
- Fix hiding of mark menu when clicking th button again (#1484944)
- Use long date format in print mode (#1485191)
- Updated TinyMCE to version 3.1.0.1
- Re-enable autocomplete attribute for login form (#1485211)
- Check PERMANENTFLAGS before saving $MDNSent flag (#1484963, #1485163)
- Added flag column on messages list (#1484623)
- Patched Mail/MimePart.php (http://pear.php.net/bugs/bug.php?id=14232)
- Allow trash/junk subfolders to be purged (#1485085)
- Store compose parameters in session and redirect to a unique URL
- Fixed CRAM-MD5 authentication (#1484819)
- Fixed forwarding messages with one HTML attachment (#1484442)
- Fixed encoding of message/rfc822 attachments and image/pjpeg handling (#1484914)
- Added option to select skin in user preferences
- Added option to configure displaying of attached images below the message body
- Added option to display images in messages from known senders (#1484601)
- User preferences grouped in more fieldsets
- Fix corrupted MIME headers of messages in Sent folder (#1485111)
- Fixed bug in MDB2 package: http://pear.php.net/bugs/bug.php?id=14124
- Use keypress instead of keydown to select list's row (#1484816)
- Don't call expunge and don't remove message row after message move if flag_for_deletion is set to true (#1485002)
RELEASE 0.2-ALPHA
-----------------
- Added option to disable autocompletion from selected LDAP address books (#1484922)
- TLS support in LDAP connections: 'use_tls' property (#1485104)
- Fixed removing messages from search set after deleting them (#1485106)
- imap.inc: Fixed iil_C_FetchStructureString() to handle many
literal strings in response (#1484969)
- Support for subfolders in default/protected folders (#1484665)
- Disallowed delimiter in folder name (#1484803)
- Support " and \ in folder names
- Escape \ in login (#1484614)
- Better HTML sanitization with the DOM-based washtml script (#1484701)
- Fixed sorting of folders with non-ascii characters
- Fixed Mysql DDL for default identities creation (#1485070)
- In Preferences added possibility to configure 'read_when_deleted',
'mdn_requests', 'flag_for_deletion' options
- Made IMAP auth type configurable (#1483825)
- Fixed empty values with FROM_UNIXTIME() in rcube_mdb2 (#1485055)
- Fixed attachment list on IE 6/7 (#1484807)
- Fixed JavaScript in compose.html that shows cc/bcc fields if populated
- Make password input fields of type password in installer (#1484886)
- Don't attempt to delete cache entries if enable_caching is FALSE (#1485051)
- Optimized messages sorting on servers without sort capability (#1485049)
- Corrected message headers decoding when charset isn't specified and improved
support for native languages (#1485050, #1485048)
- Expanded LDAP configuration options to support LDAP server writes.
- Installer: encode special characters in DB username/password (#1485042)
- Fixed management of folders with national characters in names (#1485036, #1485001)
- Fixed identities saving when using MDB2 pgsql driver (#1485032)
- Fixed BCC header reset (#1484997)
- Improved messages list performance - patch from Justin Heesemann
- Append skin_path to images location only when it starts with '/' sign (#1484859)
- Fix IMAP response in message body when message has no body (#1484964)
- Fixed non-RFC dates formatting (#1484901)
- Fixed typo in set_charset() (#1484991)
- Decode entities when inserting HTML signature to plain text message (#1484990)
- HTML editing is now working with PHP5 updates and TinyMCE v3.0.6
- Fixed signature loading on Windows (#1484545)
- Added language support to HTML editing (#1484862)
- Fixed remove signature when replying (#1333167)
- Fixed problem with line with a space at the end (#1484916)
- Fixed <!DOCTYPE> tag filtering (#1484391)
- Fixed <?xml> tag filtering (#1484403)
- Added sections (fieldset+label) in Settings interface
- Mark as read in one action with message preview (#1484972)
- Deleted redundant quota reads (#1484972)
- Added options for empty trash and expunge inbox on logout (#1483863)
- Removed lines wrapping when displaying message
- Fixed month localization
- Changed codebase to PHP5 with autoloader
RELEASE 0.1.1
-------------
- Clear selection when selecting single item (#1484942)
- Remove hard-coded image size in skin templates (#1484893)
- Database schema improvements (dropped unnecessary indexes)
- Fixed creating a new folder with a comma in its name (#1484681)
- Fixed sorting of messages when default mailbox is empty (#1484317)
- Improve message previewpane - less loading (#1484316)
- Fixed login form autoompletion (#1484839)
- Fixed virtuser_query option for mdb2 backend (#1484874)
- Fixed attachment resoting from Drafts when message body was empty (#1484506)
- Fixed usage of ob_gzhandler (#1484851)
- Fixed message part window in IE6 (#1484610)
- Fixed decoding of mime-encoded strings (#1484191)
- Fixed some iconv/mb_string problems (#1484598)
- Correctly quote mailbox name when using in URL (#1484313)
- Fixed "headers already sent" errors (#1484860)
RELEASE 0.1-STABLE
------------------
- Added interactive installer script
- Fix folder adding/renaming inspired by #1484800
- Localize folder name in page title (#1484785)
- Fix code using wrong variable name (#1484018)
- Allow to send mail with BCC recipients only
- condense TinyMCE toolbar down to one line, removing table buttons (#1484747)
- Add function to mark the selected messages as read/unread (#1457360)
- Also do charset decoding as suggested in RFC 2231 (fix #1484321)
- Show message count in folder list and hint when creating a subfolder
- Distinguish ssl and tls for imap connections (#1484667)
- Added some charset aliases to fix typical mis-labelling (#1484565)
- Remember decision to display images for a certain message during session (#1484754)
- Truncate attachment filenames to 55 characters due to an IE bug (#1484757)
- Make sending of read receipts configurable
- Respect config when localize folder names (#1484707)
- Also respect receipt and priority settings when re-opening a draft message
- Remember search results (closes #1483883), patch by the_glu
- Add Received header on outgoing mail
- Upgrade to TinyMCE 2.1.3
- Allow inserting image attachments into HTML messages while composing (#1484557)
- Implement Message-Disposition-Notification (Receipts)
- Fix overriding of session vars when register_globals is on (#1484670)
- Fix bug with case-sensitive folder names (#1484245)
- Don't create default folders by default
- Fixed some potential security risks (audited by Andris)
- Only show new messages if they match the current search (#1484176)
- Switch to/from when searcing in Sent folder (#1484555)
- Correctly read the References header (#1484646)
- Unset old cookie before sending a new value (#1484639)
- Correctly decode attachments when downloading them (#1484645 and #1484642)
- Suppress IE errors when clearing attachments form (#1484356)
- Log error when login fails due to auto_create_user turned off
- Filter linked/imported CSS files (closes #1484056)
- Improve message compose screen (closes #1484383)
- Select next row after removing one from list (#1484387)
RELEASE 0.1-RC2
---------------
- Enable drag-&-dropping of folders to a new parent and allow to create subfolders (#1457344)
- Suppress IE errors when clearing attachments form (#1484356)
- Set preferences field in user table to NULL (#1484386)
- Log error when login fails due to auto_create_user turned off
- Filter linked/imported CSS files (closes #1484056)
- Improve message compose screen (closes #1484383)
- Select next row after removing one from list (#1484387)
- Make smtp HELO/EHLO hostname configurable (#1484067)
- IPv6 Compatability (#1484322), Patch #1484373
- Unlock interface when message sending fails (#1484570)
- Eval PHP code in template includes (if configured)
- Show message when folder is empty. Mo more static text in table (#1484395)
- Only display unread count in page title when new messages arrived
- Fixed wrong delete button tooltip (#1483965)
- Fixed charset encoding bug (#1484429)
- Applied patch for LDAP version (#1484552)
- Improved XHTML validation
- Fix message list selection (#1484550)
- Better fix lowercased usernames (#1484473)
- Update pngbehavior Script as suggested in #1484490
- Fixed moving/deleting messages when more than 1 is selected
- Applied patch for LDAP contacts listing by Glen Ogilvie
- Applied patch for more address fields in LDAP contacts (#1484402)
- Add alternative for getallheaders() (fix #1484508)
- Identify mailboxes case-sensitive
- Sort mailbox list case-insensitive (closes #1484338)
- Fix display of multipart messages from Apple Mail (closes #1484027)
- Protect AJAX request from being fetched by a foreign site (XSS)
- Make autocomplete for loginform configurable by the skin template
- Fix compose function from address book (closes #1484426)
- Added //IGNORE to iconv call (patch #1484420, closes #1484023)
- Check if mbstring supports charset (#1484290 and #1484292)
- Prefer iconv over mbstring (as suggested in #1484292)
- Check filesize of template includes (#1484409)
- Fixed bug with buttons not dimming/enabling properly after switching folders
- Fixed compose window becoming unresponsive after saving a draft (#1484487)
- Re-enabled "Back" button in compose window now that bug #1484487 is fixed
- Fixed unresponsive interface issue when downloading attachments (#1484496)
- Lowered status message time from 5 to 3 seconds to improve responsiveness
- Raised .htaccess upload_max_filesize from 2M to 5M to differ from default php.ini
- Increased "mailboxcontrols" mail.css width from 160 to 170px to fix non-english languages (#1484499)
- Fix status message bug #1484464 with regard to #1484353
- Fix address adding bug reported by David Koblas
- Applied socket error patch by Thomas Mangin
- Pass-by-reference workarround for PHP5 in sendmail.inc
- Fixed buggy imap_root settings (closes #1484379)
- Prevent default events on subject links (#1484399)
- Use HTTP-POST requests for actions that change state
RELEASE 0.1-RC1
---------------
- Use global filters and bind username/ for Ldap searches (#1484159)
- Hide quota display if imap server does not support it
- Hide address groups if no LDAP servers configured
- Add link to message subjects (closes #1484257)
- Better SQL query for contact listing/search (closes #1484369)
- Fixed marking as read in preview pane (closes #1484364)
- CSS hack to display attachments correctly in IE6
- Wrap message body text (closes #1484148)
- LDAP access is back in address book (closes #1484087)
- Added search function for contacts
- New Template parsing and output encoding
- Fixed bugs #1484119 and #1483978
- Fixed message moving procedure (closes #1484308)
- Fixed display of multiple attachments (closes #1466563)
- Fixed check for new messages (closes #1484310)
- List attachments without filename
- New session authentication: Change sessid cookie when login, authentication with sessauth cookie is now configurable.
Should close bugs #1483951 and #1484299
- Correctly translate mailbox names (closes #1484276)
- Quote e-mail address links (closes #1484300)
- Updated PEAR::Mail_mime package
- Accept single quotes for HTML attributes when modifying message body (thanks Jason)
- Sanitize input for new users/identities (thanks Colin Alston)
- Don't download HTML message parts
- Convert HTML parts to plaintext if 'prefer_html' is off
- Correctly parse message/rfc822 parts (closes #1484045)
- Also use user_id for unique key in messages table (closes #1484074)
- Hide contacts drop down on blur (closes #1484203)
- Make entries in contacts drop down clickable
- Turn off browser autocompletion on login page
- Quote <? in text/html message parts
- Hide border around radio buttons
- Applied patch for attachment download by crichardson (closes #1484198)
- Fixed bug in Postgres DB handling (closes #1484068)
- Fixed bug of invalid calls to fetchRow() in rcube_db.inc (closes #1484280)
- Fixed array_merge bug (closes #1484281)
- Fixed flag for deletion in list view (closes #1484264)
- Finally support semicolons as recipient separator (closes ##1484251)
- Fixed message headers (subject) encoding
- check if safe mode is on or not (closes #1484269)
- Show "no subject" in message list if subject is missing (closes #1484243)
- Solved page caching of message preview (closes #1484153)
- Only use gzip compression if configured (closes #1484236)
- Fixed priority selector issue (#1484150)
- Fixed some CSS issues in default skin (closes #1484210 and #1484161)
- Prevent from double quoting of numeric HTML character references (closes #1484253)
- Fixed display of HTML message attachments (closes #1484178)
- Applied patch for preview caching (closes #1484186)
- Added error handling for attachment uploads
- Use multibyte safe string functions where necessary (closes #1483988)
- Applied security patch to validate the submitted host value (by Kees Cook)
- Applied security patch to validate input values when deleting contacts (by Kees Cook)
- Applied security patch that sanitizes emoticon paths when attaching them (by Kees Cook)
- Applied a patch to more aggressively sanitize a HTML message
- Visualize blocked images in HTML messages
- Fixed wrong message listing when showing search results (closes #1484131)
- Show remote images when opening HTML message part as attachment
- Improve memory usage when sending mail (closes #1484098)
- Mark messages as read once the preview is loaded (closes #1484132)
- Include smtp final response in log (closes #1484081)
- Corrected date string in sent message header (closes #1484125)
- Correclty choose "To" column in sent and draft mailboxes (closes #1483943)
- Changed srong tooltips for message browse buttons (closes #1483930)
- Fixed signature delimeter character to be standard (Bug #1484035)
- Fixed XSS vulnerability (Bug #1484109)
- Remove newlines from mail headers (Bug #1484031)
- Selection issues when moving/deleting (Bug #1484044)
- Applied patch of Clement Moulin for imap host auto-selection
- ISO-encode IMAP password for plaintext login (Bugs #1483977 & #1483886)
- Fixed folder name encoding in subscription list (Bug #1484113)
- Fixed JS errors in identity list (Bug #1484120)
- Translate foldernames in folder form (closes #1484113)
- Added first and last buttons to message list, address book
and message detail
- Pressing Shift-Del bypasses Trash folder
- Enable purge command for Junk folder
- Fetch all aliases if virtuser_query is used instead
- Re-enabled multi select of contacts (Bug #1484017)
- Enable contact editing right after creation (Bug #1459641)
- Correct UTF-7 to UTF-8 conversion if mbstring is not available
- Fixed IMAP fetch of message body (Bug #1484019)
- Fixed safe_mode problems (Bug #1418381)
- Fixed wrong header encoding (Bug #1483976)
- Made automatic draft saving configurable
- Fixed JS bug when renaming folders (Bug #1483989)
- Added quota display as image (by Brett Patterson)
- Corrected creation of a message-id
- New indentation for quoted message text
- Improved HTML validity
- Fixed URL character set (Ticket #1445501)
- Fixed saving of contact into MySQL from LDAP query results (Ticket #1483820)
- Fixed folder renaming: unsubscribe before rename (Bug #1483920)
- Finalized new message parsing (+ chaching)
- Fixed wrong usage of mbstring (Bug #1462439)
- Set default spelling language (Ticket #1483938)
- Added support for Nox Spell Server
- Re-built message parsing (Bug #1327068)
Now based on the message structure delivered by the IMAP server.
- Fixed some XSS and SQL injection issues
- Fixed charset problems with folder renaming
diff --git a/bin/msgexport.sh b/bin/msgexport.sh
index 0aa33b5a8..239d89363 100755
--- a/bin/msgexport.sh
+++ b/bin/msgexport.sh
@@ -1,139 +1,139 @@
#!/usr/bin/env php
<?php
define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' );
ini_set('memory_limit', -1);
require_once INSTALL_PATH.'program/include/clisetup.php';
function print_usage()
{
print "Usage: msgexport -h imap-host -u user-name -m mailbox name\n";
print "--host IMAP host\n";
print "--user IMAP user name\n";
print "--mbox Folder name, set to '*' for all\n";
print "--file Output file\n";
}
function vputs($str)
{
$out = $GLOBALS['args']['file'] ? STDOUT : STDERR;
fwrite($out, $str);
}
function progress_update($pos, $max)
{
$percent = round(100 * $pos / $max);
vputs(sprintf("%3d%% [%-51s] %d/%d\033[K\r", $percent, @str_repeat('=', $percent / 2) . '>', $pos, $max));
}
function export_mailbox($mbox, $filename)
{
global $IMAP;
-
+
$IMAP->set_mailbox($mbox);
-
+
vputs("Getting message list of {$mbox}...");
vputs($IMAP->messagecount()." messages\n");
-
+
if ($filename)
{
if (!($out = fopen($filename, 'w')))
{
vputs("Cannot write to output file\n");
return;
}
vputs("Writing to $filename\n");
}
else
$out = STDOUT;
-
+
for ($count = $IMAP->messagecount(), $i=1; $i <= $count; $i++)
{
$headers = $IMAP->get_headers($i, null, false);
$from = current($IMAP->decode_address_list($headers->from, 1, false));
fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid));
fwrite($out, $IMAP->conn->fetchPartHeader($mbox, $i));
fwrite($out, $IMAP->conn->handlePartBody($mbox, $i));
fwrite($out, "\n\n\n");
progress_update($i, $count);
}
vputs("\ncomplete.\n");
if ($filename)
fclose($out);
}
// get arguments
$args = get_opt(array('h' => 'host', 'u' => 'user', 'p' => 'pass', 'm' => 'mbox', 'f' => 'file')) + array('host' => 'localhost', 'mbox' => 'INBOX');
if ($_SERVER['argv'][1] == 'help')
{
print_usage();
exit;
}
else if (!$args['host'])
{
vputs("Missing required parameters.\n");
print_usage();
exit;
}
// prompt for username if not set
if (empty($args['user']))
{
vputs("IMAP user: ");
$args['user'] = trim(fgets(STDIN));
}
// prompt for password
$args['pass'] = prompt_silent("Password: ");
// parse $host URL
$a_host = parse_url($args['host']);
if ($a_host['host'])
{
$host = $a_host['host'];
$imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? TRUE : FALSE;
$imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : 143);
}
else
{
$host = $args['host'];
$imap_port = 143;
}
// instantiate IMAP class
$IMAP = new rcube_imap(null);
// try to connect to IMAP server
if ($IMAP->connect($host, $args['user'], $args['pass'], $imap_port, $imap_ssl))
{
vputs("IMAP login successful.\n");
$filename = null;
$mailboxes = $args['mbox'] == '*' ? $IMAP->list_mailboxes(null) : array($args['mbox']);
foreach ($mailboxes as $mbox)
{
if ($args['file'])
$filename = preg_replace('/\.[a-z0-9]{3,4}$/i', '', $args['file']) . asciiwords($mbox) . '.mbox';
else if ($args['mbox'] == '*')
$filename = asciiwords($mbox) . '.mbox';
if ($args['mbox'] == '*' && in_array(strtolower($mbox), array('junk','spam','trash')))
continue;
export_mailbox($mbox, $filename);
}
}
else
{
vputs("IMAP login failed.\n");
}
?>
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 9f26450a1..59ffaea89 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -1,1690 +1,1693 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/rcmail.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2008-2011, The Roundcube Dev Team |
| Copyright (C) 2011, Kolab Systems AG |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Application class providing core functions and holding |
| instances of all 'global' objects like db- and imap-connections |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
/**
* Application class of Roundcube Webmail
* implemented as singleton
*
* @package Core
*/
class rcmail
{
/**
* Main tasks.
*
* @var array
*/
static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy');
/**
* Singleton instace of rcmail
*
* @var rcmail
*/
static private $instance;
/**
* Stores instance of rcube_config.
*
* @var rcube_config
*/
public $config;
/**
* Stores rcube_user instance.
*
* @var rcube_user
*/
public $user;
/**
* Instace of database class.
*
* @var rcube_mdb2
*/
public $db;
/**
* Instace of Memcache class.
*
* @var rcube_mdb2
*/
public $memcache;
/**
* Instace of rcube_session class.
*
* @var rcube_session
*/
public $session;
/**
* Instance of rcube_smtp class.
*
* @var rcube_smtp
*/
public $smtp;
/**
* Instance of rcube_imap class.
*
* @var rcube_imap
*/
public $imap;
/**
* Instance of rcube_template class.
*
* @var rcube_template
*/
public $output;
/**
* Instance of rcube_plugin_api.
*
* @var rcube_plugin_api
*/
public $plugins;
/**
* Current task.
*
* @var string
*/
public $task;
/**
* Current action.
*
* @var string
*/
public $action = '';
public $comm_path = './';
private $texts;
private $address_books = array();
private $caches = array();
private $action_map = array();
private $shutdown_functions = array();
/**
* This implements the 'singleton' design pattern
*
* @return rcmail The one and only instance
*/
static function get_instance()
{
if (!self::$instance) {
self::$instance = new rcmail();
self::$instance->startup(); // init AFTER object was linked with self::$instance
}
return self::$instance;
}
/**
* Private constructor
*/
private function __construct()
{
// load configuration
$this->config = new rcube_config();
register_shutdown_function(array($this, 'shutdown'));
}
/**
* Initial startup function
* to register session, create database and imap connections
- *
- * @todo Remove global vars $DB, $USER
*/
private function startup()
{
// initialize syslog
if ($this->config->get('log_driver') == 'syslog') {
$syslog_id = $this->config->get('syslog_id', 'roundcube');
$syslog_facility = $this->config->get('syslog_facility', LOG_USER);
openlog($syslog_id, LOG_ODELAY, $syslog_facility);
}
// connect to database
- $GLOBALS['DB'] = $this->get_dbh();
+ $this->get_dbh();
+
+ // set global object for backward compatibility
+ $GLOBALS['DB'] = $this->db;
// start session
$this->session_init();
// create user object
$this->set_user(new rcube_user($_SESSION['user_id']));
// configure session (after user config merge!)
$this->session_configure();
// set task and action properties
$this->set_task(get_input_value('_task', RCUBE_INPUT_GPC));
$this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC));
// reset some session parameters when changing task
if ($this->task != 'utils') {
if ($this->session && $_SESSION['task'] != $this->task)
$this->session->remove('page');
// set current task to session
$_SESSION['task'] = $this->task;
}
// init output class
if (!empty($_REQUEST['_remote']))
$GLOBALS['OUTPUT'] = $this->json_init();
else
$GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed']));
// create plugin API and load plugins
$this->plugins = rcube_plugin_api::get_instance();
// init plugins
$this->plugins->init();
}
/**
* Setter for application task
*
* @param string Task to set
*/
public function set_task($task)
{
$task = asciiwords($task);
if ($this->user && $this->user->ID)
$task = !$task ? 'mail' : $task;
else
$task = 'login';
$this->task = $task;
$this->comm_path = $this->url(array('task' => $this->task));
if ($this->output)
$this->output->set_env('task', $this->task);
}
/**
* Setter for system user object
*
* @param rcube_user Current user instance
*/
public function set_user($user)
{
if (is_object($user)) {
$this->user = $user;
+
+ // set global object for backward compatibility
$GLOBALS['USER'] = $this->user;
// overwrite config with user preferences
$this->config->set_user_prefs((array)$this->user->get_prefs());
}
$_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
// set localization
setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
// workaround for http://bugs.php.net/bug.php?id=18556
if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ')))
setlocale(LC_CTYPE, 'en_US' . '.utf8');
}
/**
* Check the given string and return a valid language code
*
* @param string Language code
* @return string Valid language code
*/
private function language_prop($lang)
{
static $rcube_languages, $rcube_language_aliases;
// user HTTP_ACCEPT_LANGUAGE if no language is specified
if (empty($lang) || $lang == 'auto') {
$accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
$lang = str_replace('-', '_', $accept_langs[0]);
}
if (empty($rcube_languages)) {
@include(INSTALL_PATH . 'program/localization/index.inc');
}
// check if we have an alias for that language
if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
$lang = $rcube_language_aliases[$lang];
}
// try the first two chars
else if (!isset($rcube_languages[$lang])) {
$short = substr($lang, 0, 2);
// check if we have an alias for the short language code
if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
$lang = $rcube_language_aliases[$short];
}
// expand 'nn' to 'nn_NN'
else if (!isset($rcube_languages[$short])) {
$lang = $short.'_'.strtoupper($short);
}
}
if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
$lang = 'en_US';
}
return $lang;
}
/**
* Get the current database connection
*
* @return rcube_mdb2 Database connection object
*/
public function get_dbh()
{
if (!$this->db) {
$config_all = $this->config->all();
$this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
$this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql';
$this->db->set_debug((bool)$config_all['sql_debug']);
}
return $this->db;
}
-
-
+
+
/**
* Get global handle for memcache access
*
* @return object Memcache
*/
public function get_memcache()
{
if (!isset($this->memcache)) {
// no memcache support in PHP
if (!class_exists('Memcache')) {
$this->memcache = false;
return false;
}
$this->memcache = new Memcache;
$this->mc_available = 0;
// add alll configured hosts to pool
$pconnect = $this->config->get('memcache_pconnect', true);
foreach ($this->config->get('memcache_hosts', array()) as $host) {
list($host, $port) = explode(':', $host);
if (!$port) $port = 11211;
$this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
}
// test connection and failover (will result in $this->mc_available == 0 on complete failure)
$this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
if (!$this->mc_available)
$this->memcache = false;
}
return $this->memcache;
}
/**
* Callback for memcache failure
*/
public function memcache_failure($host, $port)
{
static $seen = array();
// only report once
if (!$seen["$host:$port"]++) {
$this->mc_available--;
raise_error(array('code' => 604, 'type' => 'db',
'line' => __LINE__, 'file' => __FILE__,
'message' => "Memcache failure on host $host:$port"),
true, false);
}
}
/**
* Initialize and get cache object
*
* @param string $name Cache identifier
* @param string $type Cache type ('db', 'apc' or 'memcache')
* @param int $ttl Expiration time for cache items in seconds
* @param bool $packed Enables/disables data serialization
*
* @return rcube_cache Cache object
*/
public function get_cache($name, $type='db', $ttl=0, $packed=true)
{
if (!isset($this->caches[$name])) {
$this->caches[$name] = new rcube_cache($type, $_SESSION['user_id'], $name, $ttl, $packed);
}
return $this->caches[$name];
}
/**
* Return instance of the internal address book class
*
* @param string Address book identifier
* @param boolean 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');
$abook_type = strtolower($this->config->get('address_book_type'));
// 'sql' is the alias for '0' used by autocomplete
if ($id == 'sql')
$id = '0';
// use existing instance
if (isset($this->address_books[$id]) && is_object($this->address_books[$id])
&& is_a($this->address_books[$id], 'rcube_addressbook')
&& (!$writeable || !$this->address_books[$id]->readonly)
) {
$contacts = $this->address_books[$id];
}
else if ($id && $ldap_config[$id]) {
$contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['imap_host']));
}
else if ($id === '0') {
$contacts = new rcube_contacts($this->db, $this->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'];
}
// get first source from the list
else if (!$id) {
$source = reset($this->get_address_sources($writeable));
if (!empty($source)) {
$contacts = $this->get_address_book($source['id']);
if ($contacts)
$id = $source['id'];
}
}
}
if (!$contacts) {
raise_error(array(
'code' => 700, 'type' => 'php',
'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;
return $contacts;
}
/**
* Return address books list
*
* @param boolean True if the address book needs to be writeable
*
* @return array Address books array
*/
public function get_address_sources($writeable = false)
{
$abook_type = strtolower($this->config->get('address_book_type'));
$ldap_config = $this->config->get('ldap_public');
$autocomplete = (array) $this->config->get('autocomplete_addressbooks');
$list = array();
// We are using the DB address book
if ($abook_type != 'ldap') {
if (!isset($this->address_books['0']))
$this->address_books['0'] = new rcube_contacts($this->db, $this->user->ID);
$list['0'] = array(
'id' => '0',
'name' => rcube_label('personaladrbook'),
'groups' => $this->address_books['0']->groups,
'readonly' => $this->address_books['0']->readonly,
'autocomplete' => in_array('sql', $autocomplete),
'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'),
);
}
if ($ldap_config) {
$ldap_config = (array) $ldap_config;
foreach ($ldap_config as $id => $prop)
$list[$id] = array(
'id' => $id,
'name' => $prop['name'],
'groups' => is_array($prop['groups']),
'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]);
}
return $list;
}
/**
* Init output object for GUI and add common scripts.
* This will instantiate a rcmail_template object and set
* environment vars according to the current session and configuration
*
* @param boolean True if this request is loaded in a (i)frame
* @return rcube_template Reference to HTML output object
*/
public function load_gui($framed = false)
{
// init output page
if (!($this->output instanceof rcube_template))
$this->output = new rcube_template($this->task, $framed);
// set keep-alive/check-recent interval
if ($this->session && ($keep_alive = $this->session->get_keep_alive())) {
$this->output->set_env('keep_alive', $keep_alive);
}
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(RCMAIL_CHARSET);
// add some basic labels to client
$this->output->add_label('loading', 'servererror');
return $this->output;
}
/**
* Create an output object for JSON responses
*
* @return rcube_json_output Reference to JSON output object
*/
public function json_init()
{
if (!($this->output instanceof rcube_json_output))
$this->output = new rcube_json_output($this->task);
return $this->output;
}
/**
* Create SMTP object and connect to server
*
* @param boolean True if connection should be established
*/
public function smtp_init($connect = false)
{
$this->smtp = new rcube_smtp();
if ($connect)
$this->smtp->connect();
}
/**
* Create global IMAP object and connect to server
*
* @param boolean True if connection should be established
*/
public function imap_init($connect = false)
{
// already initialized
if (is_object($this->imap))
return;
$this->imap = new rcube_imap();
$this->imap->skip_deleted = $this->config->get('skip_deleted');
// enable caching of imap data
$imap_cache = $this->config->get('imap_cache');
$messages_cache = $this->config->get('messages_cache');
// for backward compatybility
if ($imap_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
$imap_cache = 'db';
$messages_cache = true;
}
if ($imap_cache)
$this->imap->set_caching($imap_cache);
if ($messages_cache)
$this->imap->set_messages_caching(true);
// set pagesize from config
$this->imap->set_pagesize($this->config->get('pagesize', 50));
// Setting root and delimiter before establishing the connection
// can save time detecting them using NAMESPACE and LIST
$options = array(
'auth_type' => $this->config->get('imap_auth_type', 'check'),
'auth_cid' => $this->config->get('imap_auth_cid'),
'auth_pw' => $this->config->get('imap_auth_pw'),
'debug' => (bool) $this->config->get('imap_debug', 0),
'force_caps' => (bool) $this->config->get('imap_force_caps'),
'timeout' => (int) $this->config->get('imap_timeout', 0),
);
$this->imap->set_options($options);
// set global object for backward compatibility
$GLOBALS['IMAP'] = $this->imap;
$hook = $this->plugins->exec_hook('imap_init', array('fetch_headers' => $this->imap->fetch_add_headers));
if ($hook['fetch_headers'])
$this->imap->fetch_add_headers = $hook['fetch_headers'];
// support this parameter for backward compatibility but log warning
if ($connect) {
$this->imap_connect();
raise_error(array(
'code' => 800, 'type' => 'imap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "rcube::imap_init(true) is deprecated, use rcube::imap_connect() instead"),
true, false);
}
}
/**
* Connect to IMAP server with stored session data
*
* @return bool True on success, false on error
*/
public function imap_connect()
{
if (!$this->imap)
$this->imap_init();
if ($_SESSION['imap_host'] && !$this->imap->conn->connected()) {
if (!$this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl'])) {
if ($this->output)
$this->output->show_message($this->imap->get_error_code() == -1 ? 'imaperror' : 'sessionerror', 'error');
}
else {
$this->set_imap_prop();
return $this->imap->conn;
}
}
return false;
}
/**
* Create session object and start the session.
*/
public function session_init()
{
// session started (Installer?)
if (session_id())
return;
$sess_name = $this->config->get('session_name');
$sess_domain = $this->config->get('session_domain');
$lifetime = $this->config->get('session_lifetime', 0) * 60;
// set session domain
if ($sess_domain) {
ini_set('session.cookie_domain', $sess_domain);
}
// set session garbage collecting time according to session_lifetime
if ($lifetime) {
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
ini_set('session.cookie_secure', rcube_https_check());
ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.serialize_handler', 'php');
// use database for storing session data
$this->session = new rcube_session($this->get_dbh(), $this->config);
$this->session->register_gc_handler('rcmail_temp_gc');
if ($this->config->get('enable_caching'))
$this->session->register_gc_handler('rcmail_cache_gc');
// start PHP session (if not in CLI mode)
if ($_SERVER['REMOTE_ADDR'])
session_start();
// set initial session vars
if (!$_SESSION['user_id'])
$_SESSION['temp'] = true;
}
/**
* Configure session object internals
*/
public function session_configure()
{
if (!$this->session)
return;
$lifetime = $this->config->get('session_lifetime', 0) * 60;
// set keep-alive/check-recent interval
if ($keep_alive = $this->config->get('keep_alive')) {
// be sure that it's less than session lifetime
if ($lifetime)
$keep_alive = min($keep_alive, $lifetime - 30);
$keep_alive = max(60, $keep_alive);
$this->session->set_keep_alive($keep_alive);
}
$this->session->set_secret($this->config->get('des_key') . $_SERVER['HTTP_USER_AGENT']);
$this->session->set_ip_check($this->config->get('ip_check'));
}
/**
* Perfom login to the IMAP server and to the webmail service.
* This will also create a new user entry if auto_create_user is configured.
*
* @param string IMAP user name
* @param string IMAP password
* @param string IMAP host
* @return boolean True on success, False on failure
*/
function login($username, $pass, $host=NULL)
{
$user = NULL;
$config = $this->config->all();
if (!$host)
$host = $config['default_host'];
// Validate that selected host is in the list of configured hosts
if (is_array($config['default_host'])) {
$allowed = false;
foreach ($config['default_host'] as $key => $host_allowed) {
if (!is_numeric($key))
$host_allowed = $key;
if ($host == $host_allowed) {
$allowed = true;
break;
}
}
if (!$allowed)
return false;
}
else if (!empty($config['default_host']) && $host != rcube_parse_host($config['default_host']))
return false;
// parse $host URL
$a_host = parse_url($host);
if ($a_host['host']) {
$host = $a_host['host'];
$imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
if (!empty($a_host['port']))
$imap_port = $a_host['port'];
else if ($imap_ssl && $imap_ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143))
$imap_port = 993;
}
$imap_port = $imap_port ? $imap_port : $config['default_port'];
/* Modify username with domain if required
Inspired by Marco <P0L0_notspam_binware.org>
*/
// Check if we need to add domain
if (!empty($config['username_domain']) && strpos($username, '@') === false) {
if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
$username .= '@'.rcube_parse_host($config['username_domain'][$host], $host);
else if (is_string($config['username_domain']))
$username .= '@'.rcube_parse_host($config['username_domain'], $host);
}
// Convert username to lowercase. If IMAP backend
// is case-insensitive we need to store always the same username (#1487113)
if ($config['login_lc']) {
$username = mb_strtolower($username);
}
// 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_idn_to_ascii($host);
if (strpos($username, '@')) {
// lowercase domain name
list($local, $domain) = explode('@', $username);
$username = $local . '@' . mb_strtolower($domain);
$username = rcube_idn_to_ascii($username);
}
// user already registered -> overwrite username
if ($user = rcube_user::query($username, $host))
$username = $user->data['username'];
if (!$this->imap)
$this->imap_init();
// try IMAP login
if (!($imap_login = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl))) {
// try with lowercase
$username_lc = mb_strtolower($username);
if ($username_lc != $username) {
// try to find user record again -> overwrite username
if (!$user && ($user = rcube_user::query($username_lc, $host)))
$username_lc = $user->data['username'];
if ($imap_login = $this->imap->connect($host, $username_lc, $pass, $imap_port, $imap_ssl))
$username = $username_lc;
}
}
// exit if IMAP login failed
if (!$imap_login)
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 ($config['auto_create_user']) {
if ($created = rcube_user::create($username, $host)) {
$user = $created;
}
else {
raise_error(array(
'code' => 620, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to create a user record. Maybe aborted by a plugin?"
), true, false);
}
}
else {
raise_error(array(
'code' => 621, 'type' => 'php',
'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_imap_prop();
$this->session_configure();
// fix some old settings according to namespace prefix
$this->fix_namespace_settings($user);
// create default folders on first login
if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) {
$this->imap->create_default_folders();
}
// set session vars
$_SESSION['user_id'] = $user->ID;
$_SESSION['username'] = $user->data['username'];
$_SESSION['imap_host'] = $host;
$_SESSION['imap_port'] = $imap_port;
$_SESSION['imap_ssl'] = $imap_ssl;
$_SESSION['password'] = $this->encrypt($pass);
$_SESSION['login_time'] = mktime();
if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
$_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
if (isset($_REQUEST['_dstactive']) && $_REQUEST['_dstactive'] != '_default_')
$_SESSION['dst_active'] = intval($_REQUEST['_dstactive']);
// force reloading complete list of subscribed mailboxes
$this->imap->clear_cache('mailboxes', true);
return true;
}
return false;
}
/**
* Set root dir and last stored mailbox
* This must be done AFTER connecting to the server!
*/
public function set_imap_prop()
{
$this->imap->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
if ($default_folders = $this->config->get('default_imap_folders')) {
$this->imap->set_default_mailboxes($default_folders);
}
if (isset($_SESSION['mbox'])) {
$this->imap->set_mailbox($_SESSION['mbox']);
}
if (isset($_SESSION['page'])) {
$this->imap->set_page($_SESSION['page']);
}
}
/**
* 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 = get_input_value('_host', RCUBE_INPUT_POST);
// 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
list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST));
if (!empty($domain)) {
foreach ($default_host as $imap_host => $mail_domains) {
if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
$host = $imap_host;
break;
}
}
}
// take the first entry if $host is still an array
if (empty($host)) {
$host = array_shift($default_host);
}
}
else if (empty($default_host)) {
$host = get_input_value('_host', RCUBE_INPUT_POST);
}
else
$host = rcube_parse_host($default_host);
return $host;
}
/**
* Get localized text in the desired language
*
* @param mixed $attrib Named parameters array or label name
* @param string $domain Label domain (plugin) name
*
* @return string Localized text
*/
public function gettext($attrib, $domain=null)
{
// load localization files if not done yet
if (empty($this->texts))
$this->load_language();
// extract attributes
if (is_string($attrib))
$attrib = array('name' => $attrib);
$name = $attrib['name'] ? $attrib['name'] : '';
// attrib contain text values: use them from now
if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))
$this->texts[$name] = $setval;
// check for text with domain
if ($domain && ($text = $this->texts[$domain.'.'.$name]))
;
// text does not exist
else if (!($text = $this->texts[$name])) {
return "[$name]";
}
// replace vars in text
if (is_array($attrib['vars'])) {
foreach ($attrib['vars'] as $var_key => $var_value)
$text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text);
}
// format output
if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
return ucfirst($text);
else if ($attrib['uppercase'])
return mb_strtoupper($text);
else if ($attrib['lowercase'])
return mb_strtolower($text);
return strtr($text, array('\n' => "\n"));
}
/**
* Check if the given text label exists
*
* @param string $name Label name
* @param string $domain Label domain (plugin) name or '*' for all domains
* @param string $ref_domain Sets domain name if label is found
*
* @return boolean True if text exists (either in the current language or in en_US)
*/
public function text_exists($name, $domain = null, &$ref_domain = null)
{
// load localization files if not done yet
if (empty($this->texts))
$this->load_language();
if (isset($this->texts[$name])) {
$ref_domain = '';
return true;
}
// any of loaded domains (plugins)
if ($domain == '*') {
foreach ($this->plugins->loaded_plugins() as $domain)
if (isset($this->texts[$domain.'.'.$name])) {
$ref_domain = $domain;
return true;
}
}
// specified domain
else if ($domain) {
$ref_domain = $domain;
return isset($this->texts[$domain.'.'.$name]);
}
return false;
}
/**
* Load a localization package
*
* @param string Language ID
*/
public function load_language($lang = null, $add = array())
{
$lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
// load localized texts
if (empty($this->texts) || $lang != $_SESSION['language']) {
$this->texts = array();
// handle empty lines after closing PHP tag in localization files
ob_start();
// get english labels (these should be complete)
@include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
@include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
if (is_array($labels))
$this->texts = $labels;
if (is_array($messages))
$this->texts = array_merge($this->texts, $messages);
// include user language files
if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');
include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');
if (is_array($labels))
$this->texts = array_merge($this->texts, $labels);
if (is_array($messages))
$this->texts = array_merge($this->texts, $messages);
}
ob_end_clean();
$_SESSION['language'] = $lang;
}
// append additional texts (from plugin)
if (is_array($add) && !empty($add))
$this->texts += $add;
}
/**
* Read directory program/localization and return a list of available languages
*
* @return array List of available localizations
*/
public function list_languages()
{
static $sa_languages = array();
if (!sizeof($sa_languages)) {
@include(INSTALL_PATH . 'program/localization/index.inc');
if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
while (($name = readdir($dh)) !== false) {
if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))
continue;
if ($label = $rcube_languages[$name])
$sa_languages[$name] = $label;
}
closedir($dh);
}
}
return $sa_languages;
}
/**
* 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();
}
/**
* Do server side actions on logout
*/
public function logout_actions()
{
$config = $this->config->all();
// on logout action we're not connected to imap server
if (($config['logout_purge'] && !empty($config['trash_mbox'])) || $config['logout_expunge']) {
if (!$this->session->check_auth())
return;
$this->imap_connect();
}
if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
$this->imap->clear_mailbox($config['trash_mbox']);
}
if ($config['logout_expunge']) {
$this->imap->expunge('INBOX');
}
// Try to save unsaved user preferences
if (!empty($_SESSION['preferences'])) {
$this->user->save_prefs(unserialize($_SESSION['preferences']));
}
}
/**
* Function to be executed in script shutdown
* Registered with register_shutdown_function()
*/
public function shutdown()
{
foreach ($this->shutdown_functions as $function)
call_user_func($function);
if (is_object($this->smtp))
$this->smtp->disconnect();
foreach ($this->address_books as $book) {
if (is_object($book) && is_a($book, 'rcube_addressbook'))
$book->close();
}
foreach ($this->caches as $cache) {
if (is_object($cache))
$cache->close();
}
if (is_object($this->imap))
$this->imap->close();
// before closing the database connection, write session data
if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
session_write_close();
}
// write performance stats to logs/console
if ($this->config->get('devel_mode')) {
if (function_exists('memory_get_usage'))
$mem = show_bytes(memory_get_usage());
if (function_exists('memory_get_peak_usage'))
$mem .= '/'.show_bytes(memory_get_peak_usage());
$log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : '');
if (defined('RCMAIL_START'))
rcube_print_time(RCMAIL_START, $log);
else
console($log);
}
}
/**
* Registers shutdown function to be executed on shutdown.
* The functions will be executed before destroying any
* objects like smtp, imap, session, etc.
*
* @param callback Function callback
*/
public function add_shutdown_function($function)
{
$this->shutdown_functions[] = $function;
}
/**
* Generate a unique token to be used in a form request
*
* @return string The request token
*/
public function get_request_token()
{
$sess_id = $_COOKIE[ini_get('session.name')];
if (!$sess_id) $sess_id = session_id();
$plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->user->ID . $this->config->get('des_key') . $sess_id)));
return $plugin['value'];
}
/**
* Check if the current request contains a valid token
*
* @param int Request method
* @return boolean True if request token is valid false if not
*/
public function check_request($mode = RCUBE_INPUT_POST)
{
$token = get_input_value('_token', $mode);
$sess_id = $_COOKIE[ini_get('session.name')];
return !empty($sess_id) && $token == $this->get_request_token();
}
/**
* Create unique authorization hash
*
* @param string Session ID
* @param int Timestamp
* @return string The generated auth hash
*/
private function get_auth_hash($sess_id, $ts)
{
$auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
$sess_id,
$ts,
$this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
$_SERVER['HTTP_USER_AGENT']);
if (function_exists('sha1'))
return sha1($auth_string);
else
return md5($auth_string);
}
/**
* Encrypt using 3DES
*
* @param string $clear clear text input
* @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
* @param boolean $base64 whether or not to base64_encode() the result before returning
*
* @return string encrypted text
*/
public function encrypt($clear, $key = 'des_key', $base64 = true)
{
if (!$clear)
return '';
/*-
* Add a single canary byte to the end of the clear text, which
* will help find out how much of padding will need to be removed
* upon decryption; see http://php.net/mcrypt_generic#68082
*/
$clear = pack("a*H2", $clear, "80");
if (function_exists('mcrypt_module_open') &&
($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))
{
$iv = $this->create_iv(mcrypt_enc_get_iv_size($td));
mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
$cipher = $iv . mcrypt_generic($td, $clear);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
else {
@include_once 'des.inc';
if (function_exists('des')) {
$des_iv_size = 8;
$iv = $this->create_iv($des_iv_size);
$cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);
}
else {
raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available"
), true, true);
}
}
return $base64 ? base64_encode($cipher) : $cipher;
}
/**
* Decrypt 3DES-encrypted string
*
* @param string $cipher encrypted text
* @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
* @param boolean $base64 whether or not input is base64-encoded
*
* @return string decrypted text
*/
public function decrypt($cipher, $key = 'des_key', $base64 = true)
{
if (!$cipher)
return '';
$cipher = $base64 ? base64_decode($cipher) : $cipher;
if (function_exists('mcrypt_module_open') &&
($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))
{
$iv_size = mcrypt_enc_get_iv_size($td);
$iv = substr($cipher, 0, $iv_size);
// session corruption? (#1485970)
if (strlen($iv) < $iv_size)
return '';
$cipher = substr($cipher, $iv_size);
mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
$clear = mdecrypt_generic($td, $cipher);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
else {
@include_once 'des.inc';
if (function_exists('des')) {
$des_iv_size = 8;
$iv = substr($cipher, 0, $des_iv_size);
$cipher = substr($cipher, $des_iv_size);
$clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);
}
else {
raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"
), true, true);
}
}
/*-
* Trim PHP's padding and the canary byte; see note in
* rcmail::encrypt() and http://php.net/mcrypt_generic#68082
*/
$clear = substr(rtrim($clear, "\0"), 0, -1);
return $clear;
}
/**
* Generates encryption initialization vector (IV)
*
* @param int Vector size
* @return string Vector string
*/
private function create_iv($size)
{
// mcrypt_create_iv() can be slow when system lacks entrophy
// we'll generate IV vector manually
$iv = '';
for ($i = 0; $i < $size; $i++)
$iv .= chr(mt_rand(0, 255));
return $iv;
}
/**
* Build a valid URL to this instance of Roundcube
*
* @param mixed Either a string with the action or url parameters as key-value pairs
* @return string Valid application URL
*/
public function url($p)
{
if (!is_array($p))
$p = array('_action' => @func_get_arg(0));
$task = $p['_task'] ? $p['_task'] : ($p['task'] ? $p['task'] : $this->task);
$p['_task'] = $task;
unset($p['task']);
$url = './';
$delm = '?';
foreach (array_reverse($p) as $key => $val) {
if ($val !== '') {
$par = $key[0] == '_' ? $key : '_'.$key;
$url .= $delm.urlencode($par).'='.urlencode($val);
$delm = '&';
}
}
return $url;
}
/**
* Use imagemagick or GD lib to read image properties
*
* @param string Absolute file path
* @return mixed Hash array with image props like type, width, height or False on error
*/
public static function imageprops($filepath)
{
$rcmail = rcmail::get_instance();
if ($cmd = $rcmail->config->get('im_identify_path', false)) {
list(, $type, $size) = explode(' ', strtolower(rcmail::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath))));
if ($size)
list($width, $height) = explode('x', $size);
}
else if (function_exists('getimagesize')) {
$imsize = @getimagesize($filepath);
$width = $imsize[0];
$height = $imsize[1];
$type = preg_replace('!image/!', '', $imsize['mime']);
}
return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false;
}
/**
* Convert an image to a given size and type using imagemagick (ensures input is an image)
*
* @param $p['in'] Input filename (mandatory)
* @param $p['out'] Output filename (mandatory)
* @param $p['size'] Width x height of resulting image, e.g. "160x60"
* @param $p['type'] Output file type, e.g. "jpg"
* @param $p['-opts'] Custom command line options to ImageMagick convert
* @return Success of convert as true/false
*/
public static function imageconvert($p)
{
$result = false;
$rcmail = rcmail::get_instance();
$convert = $rcmail->config->get('im_convert_path', false);
$identify = $rcmail->config->get('im_identify_path', false);
// imagemagick is required for this
if (!$convert)
return false;
if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false))))
list(, $type) = explode(' ', strtolower(rcmail::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps
$type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps"));
$p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);
$p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts'];
if (in_array($type, explode(',', $p['types']))) # Valid type?
$result = rcmail::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === "";
return $result;
}
/**
* Construct shell command, execute it and return output as string.
* Keywords {keyword} are replaced with arguments
*
* @param $cmd Format string with {keywords} to be replaced
* @param $values (zero, one or more arrays can be passed)
* @return output of command. shell errors not detectable
*/
public static function exec(/* $cmd, $values1 = array(), ... */)
{
$args = func_get_args();
$cmd = array_shift($args);
$values = $replacements = array();
// merge values into one array
foreach ($args as $arg)
$values += (array)$arg;
preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER);
foreach ($matches as $tags) {
list(, $tag, $option, $key) = $tags;
$parts = array();
if ($option) {
foreach ((array)$values["-$key"] as $key => $value) {
if ($value === true || $value === false || $value === null)
$parts[] = $value ? $key : "";
else foreach ((array)$value as $val)
$parts[] = "$key " . escapeshellarg($val);
}
}
else {
foreach ((array)$values[$key] as $value)
$parts[] = escapeshellarg($value);
}
$replacements[$tag] = join(" ", $parts);
}
// use strtr behaviour of going through source string once
$cmd = strtr($cmd, $replacements);
return (string)shell_exec($cmd);
}
/**
* 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();
setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'],
rcube_https_check(), 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->imap->get_namespace('prefix');
$prefix_len = strlen($prefix);
if (!$prefix_len)
return;
$prefs = $this->config->all();
if (!empty($prefs['namespace_fixed']))
return;
// Build namespace prefix regexp
$ns = $this->imap->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 = $prefs[$opt]) {
if ($value != 'INBOX' && !preg_match($regexp, $value)) {
$prefs[$opt] = $prefix.$value;
}
}
}
if (!empty($prefs['default_imap_folders'])) {
foreach ($prefs['default_imap_folders'] as $idx => $name) {
if ($name != 'INBOX' && !preg_match($regexp, $name)) {
$prefs['default_imap_folders'][$idx] = $prefix.$name;
}
}
}
if (!empty($prefs['search_mods'])) {
$folders = array();
foreach ($prefs['search_mods'] as $idx => $value) {
if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) {
$idx = $prefix.$idx;
}
$folders[$idx] = $value;
}
$prefs['search_mods'] = $folders;
}
if (!empty($prefs['message_threading'])) {
$folders = array();
foreach ($prefs['message_threading'] as $idx => $value) {
if ($idx != 'INBOX' && !preg_match($regexp, $idx)) {
$idx = $prefix.$idx;
}
$folders[$prefix.$idx] = $value;
}
$prefs['message_threading'] = $folders;
}
if (!empty($prefs['collapsed_folders'])) {
$folders = explode('&&', $prefs['collapsed_folders']);
$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_imap_prop();
}
}
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index c3863e448..08503227c 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -1,1497 +1,1497 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/mail/compose.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2011, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Compose a new mail message with all headers and attachments |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// define constants for message compose mode
define('RCUBE_COMPOSE_REPLY', 0x0106);
define('RCUBE_COMPOSE_FORWARD', 0x0107);
define('RCUBE_COMPOSE_DRAFT', 0x0108);
define('RCUBE_COMPOSE_EDIT', 0x0109);
$MESSAGE_FORM = null;
$MESSAGE = null;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET);
$COMPOSE = null;
if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID])
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
// give replicated session storage some time to synchronize
$retries = 0;
while ($COMPOSE_ID && !is_array($COMPOSE) && $RCMAIL->db->is_replicated() && $retries++ < 5) {
usleep(500000);
$RCMAIL->session->reload();
if ($_SESSION['compose_data_'.$COMPOSE_ID])
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
}
// Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
// if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
if (!is_array($COMPOSE))
{
// Infinite redirect prevention in case of broken session (#1487028)
if ($COMPOSE_ID)
raise_error(array('code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Invalid compose ID"), true, true);
$COMPOSE_ID = uniqid(mt_rand());
$_SESSION['compose_data_'.$COMPOSE_ID] = array(
'id' => $COMPOSE_ID,
'param' => request2param(RCUBE_INPUT_GET),
'mailbox' => $RCMAIL->imap->get_mailbox_name(),
);
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
// process values like "mailto:foo@bar.com?subject=new+message&cc=another"
if ($COMPOSE['param']['to']) {
// #1486037: remove "mailto:" prefix
$COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $COMPOSE['param']['to']);
$mailto = explode('?', $COMPOSE['param']['to']);
if (count($mailto) > 1) {
$COMPOSE['param']['to'] = $mailto[0];
parse_str($mailto[1], $query);
foreach ($query as $f => $val)
$COMPOSE['param'][$f] = $val;
}
}
// select folder where to save the sent message
$COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox');
// pipe compose parameters thru plugins
$plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE);
$COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']);
// add attachments listed by message_compose hook
if (is_array($plugin['attachments'])) {
foreach ($plugin['attachments'] as $attach) {
// we have structured data
if (is_array($attach)) {
$attachment = $attach;
}
// only a file path is given
else {
$filename = basename($attach);
$attachment = array(
'group' => $COMPOSE_ID,
'name' => $filename,
'mimetype' => rc_mime_content_type($attach, $filename),
'path' => $attach,
);
}
// save attachment if valid
if (($attachment['data'] && $attachment['name']) || ($attachment['path'] && file_exists($attachment['path']))) {
$attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
}
if ($attachment['status'] && !$attachment['abort']) {
unset($attachment['data'], $attachment['status'], $attachment['abort']);
$COMPOSE['attachments'][$attachment['id']] = $attachment;
}
}
}
// check if folder for saving sent messages exists and is subscribed (#1486802)
if ($sent_folder = $COMPOSE['param']['sent_mbox']) {
rcmail_check_sent_folder($sent_folder, true);
}
// redirect to a unique URL with all parameters stored in session
$OUTPUT->redirect(array('_action' => 'compose', '_id' => $COMPOSE['id']));
}
// add some labels to client
$OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning', 'cancel',
'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage',
'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
'fileuploaderror', 'sendmessage');
$OUTPUT->set_env('compose_id', $COMPOSE['id']);
// add config parameters to client script
if (!empty($CONFIG['drafts_mbox'])) {
$OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
$OUTPUT->set_env('draft_autosave', $CONFIG['draft_autosave']);
}
// set current mailbox in client environment
$OUTPUT->set_env('mailbox', $RCMAIL->imap->get_mailbox_name());
$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false));
$OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false));
$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
// use jquery UI for showing prompt() dialogs
$RCMAIL->plugins->load_plugin('jqueryui');
// default font for HTML editor
$font = rcube_fontdefs($RCMAIL->config->get('default_font', 'Verdana'));
if ($font && !is_array($font)) {
$OUTPUT->set_env('default_font', $font);
}
// get reference message and set compose mode
if ($msg_uid = $COMPOSE['param']['draft_uid']) {
$RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
$compose_mode = RCUBE_COMPOSE_DRAFT;
}
else if ($msg_uid = $COMPOSE['param']['reply_uid'])
$compose_mode = RCUBE_COMPOSE_REPLY;
else if ($msg_uid = $COMPOSE['param']['forward_uid'])
$compose_mode = RCUBE_COMPOSE_FORWARD;
else if ($msg_uid = $COMPOSE['param']['uid'])
$compose_mode = RCUBE_COMPOSE_EDIT;
$config_show_sig = $RCMAIL->config->get('show_sig', 1);
if ($config_show_sig == 1)
$OUTPUT->set_env('show_sig', true);
else if ($config_show_sig == 2 && (empty($compose_mode) || $compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT))
$OUTPUT->set_env('show_sig', true);
else if ($config_show_sig == 3 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD))
$OUTPUT->set_env('show_sig', true);
else
$OUTPUT->set_env('show_sig', false);
// set line length for body wrapping
$LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
if (!empty($msg_uid))
{
// similar as in program/steps/mail/show.inc
// re-set 'prefer_html' to have possibility to use html part for compose
$CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
$MESSAGE = new rcube_message($msg_uid);
// make sure message is marked as read
if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']))
$RCMAIL->imap->set_flag($msg_uid, 'SEEN');
if (!empty($MESSAGE->headers->charset))
$RCMAIL->imap->set_charset($MESSAGE->headers->charset);
if ($compose_mode == RCUBE_COMPOSE_REPLY)
{
$COMPOSE['reply_uid'] = $msg_uid;
$COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID;
$COMPOSE['references'] = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
if (!empty($COMPOSE['param']['all']))
$MESSAGE->reply_all = $COMPOSE['param']['all'];
$OUTPUT->set_env('compose_mode', 'reply');
// Save the sent message in the same folder of the message being replied to
if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $COMPOSE['mailbox'])
&& rcmail_check_sent_folder($sent_folder, false)
) {
$COMPOSE['param']['sent_mbox'] = $sent_folder;
}
}
else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
{
if ($MESSAGE->headers->others['x-draft-info'])
{
// get reply_uid/forward_uid to flag the original message when sending
$info = rcmail_draftinfo_decode($MESSAGE->headers->others['x-draft-info']);
if ($info['type'] == 'reply')
$COMPOSE['reply_uid'] = $info['uid'];
else if ($info['type'] == 'forward')
$COMPOSE['forward_uid'] = $info['uid'];
$COMPOSE['mailbox'] = $info['folder'];
// Save the sent message in the same folder of the message being replied to
if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $info['folder'])
&& rcmail_check_sent_folder($sent_folder, false)
) {
$COMPOSE['param']['sent_mbox'] = $sent_folder;
}
}
if ($MESSAGE->headers->in_reply_to)
$COMPOSE['reply_msgid'] = '<'.$MESSAGE->headers->in_reply_to.'>';
$COMPOSE['references'] = $MESSAGE->headers->references;
}
else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
{
$COMPOSE['forward_uid'] = $msg_uid;
$OUTPUT->set_env('compose_mode', 'forward');
if (!empty($COMPOSE['param']['attachment']))
$MESSAGE->forward_attachment = true;
}
}
$MESSAGE->compose = array();
// get user's identities
-$MESSAGE->identities = $USER->list_identities();
+$MESSAGE->identities = $RCMAIL->user->list_identities();
if (count($MESSAGE->identities))
{
foreach ($MESSAGE->identities as $idx => $ident) {
$email = mb_strtolower(rcube_idn_to_utf8($ident['email']));
$MESSAGE->identities[$idx]['email_ascii'] = $ident['email'];
$MESSAGE->identities[$idx]['ident'] = format_email_recipient($ident['email'], $ident['name']);
$MESSAGE->identities[$idx]['email'] = $email;
}
}
// Set From field value
if (!empty($_POST['_from'])) {
$MESSAGE->compose['from'] = get_input_value('_from', RCUBE_INPUT_POST);
}
else if (!empty($COMPOSE['param']['from'])) {
$MESSAGE->compose['from'] = $COMPOSE['param']['from'];
}
else if (count($MESSAGE->identities)) {
$a_recipients = array();
$a_names = array();
// extract all recipients of the reply-message
if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD)))
{
$a_to = $RCMAIL->imap->decode_address_list($MESSAGE->headers->to);
foreach ($a_to as $addr) {
if (!empty($addr['mailto'])) {
$a_recipients[] = strtolower($addr['mailto']);
$a_names[] = $addr['name'];
}
}
if (!empty($MESSAGE->headers->cc)) {
$a_cc = $RCMAIL->imap->decode_address_list($MESSAGE->headers->cc);
foreach ($a_cc as $addr) {
if (!empty($addr['mailto'])) {
$a_recipients[] = strtolower($addr['mailto']);
$a_names[] = $addr['name'];
}
}
}
}
$from_idx = null;
$default_identity = null;
$return_path = $MESSAGE->headers->others['return-path'];
// Select identity
foreach ($MESSAGE->identities as $idx => $ident) {
// save default identity ID
if ($ident['standard']) {
$default_identity = $idx;
}
// use From header
if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
if ($MESSAGE->headers->from == $ident['ident']) {
$from_idx = $idx;
break;
}
}
// reply to yourself
else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) {
$from_idx = $idx;
break;
}
// use replied message recipients
else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
// match identity name, prefer default identity
if ($from_idx === null || ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name'])) {
$from_idx = $idx;
}
}
}
// Fallback using Return-Path
if ($from_idx === null && $return_path) {
foreach ($MESSAGE->identities as $idx => $ident) {
if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) {
$from_idx = $idx;
break;
}
}
}
// Still no ID, use default/first identity
if ($from_idx === null) {
$from_idx = $default_identity !== null ? $default_identity : key(reset($MESSAGE->identities));
}
$ident = $MESSAGE->identities[$from_idx];
$from_id = $ident['identity_id'];
$MESSAGE->compose['from_email'] = $ident['email'];
$MESSAGE->compose['from'] = $from_id;
}
// Set other headers
$a_recipients = array();
$parts = array('to', 'cc', 'bcc', 'replyto', 'followupto');
$separator = trim($RCMAIL->config->get('recipients_separator', ',')) . ' ';
foreach ($parts as $header) {
$fvalue = '';
$decode_header = true;
// we have a set of recipients stored is session
if ($header == 'to' && ($mailto_id = $COMPOSE['param']['mailto'])
&& $_SESSION['mailto'][$mailto_id]
) {
$fvalue = urldecode($_SESSION['mailto'][$mailto_id]);
$decode_header = false;
// make session to not grow up too much
unset($_SESSION['mailto'][$mailto_id]);
$COMPOSE['param']['to'] = $fvalue;
}
else if (!empty($_POST['_'.$header])) {
$fvalue = get_input_value('_'.$header, RCUBE_INPUT_POST, TRUE);
}
else if (!empty($COMPOSE['param'][$header])) {
$fvalue = $COMPOSE['param'][$header];
}
else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
// get recipent address(es) out of the message headers
if ($header == 'to') {
$mailfollowup = $MESSAGE->headers->others['mail-followup-to'];
$mailreplyto = $MESSAGE->headers->others['mail-reply-to'];
// Reply to mailing list...
if ($MESSAGE->reply_all == 'list' && $mailfollowup)
$fvalue = $mailfollowup;
else if ($MESSAGE->reply_all == 'list'
&& preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m))
$fvalue = $m[1];
// Reply to...
else if ($MESSAGE->reply_all && $mailfollowup)
$fvalue = $mailfollowup;
else if ($mailreplyto)
$fvalue = $mailreplyto;
else if (!empty($MESSAGE->headers->replyto))
$fvalue = $MESSAGE->headers->replyto;
else if (!empty($MESSAGE->headers->from))
$fvalue = $MESSAGE->headers->from;
// Reply to message sent by yourself (#1487074)
if (!empty($ident) && $fvalue == $ident['ident']) {
$fvalue = $MESSAGE->headers->to;
}
}
// add recipient of original message if reply to all
else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') {
if ($v = $MESSAGE->headers->to)
$fvalue .= $v;
if ($v = $MESSAGE->headers->cc)
$fvalue .= (!empty($fvalue) ? $separator : '') . $v;
}
}
else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
// get drafted headers
if ($header=='to' && !empty($MESSAGE->headers->to))
$fvalue = $MESSAGE->get_header('to');
else if ($header=='cc' && !empty($MESSAGE->headers->cc))
$fvalue = $MESSAGE->get_header('cc');
else if ($header=='bcc' && !empty($MESSAGE->headers->bcc))
$fvalue = $MESSAGE->get_header('bcc');
else if ($header=='replyto' && !empty($MESSAGE->headers->others['mail-reply-to']))
$fvalue = $MESSAGE->get_header('mail-reply-to');
else if ($header=='replyto' && !empty($MESSAGE->headers->replyto))
$fvalue = $MESSAGE->get_header('reply-to');
else if ($header=='followupto' && !empty($MESSAGE->headers->others['mail-followup-to']))
$fvalue = $MESSAGE->get_header('mail-followup-to');
}
// split recipients and put them back together in a unique way
if (!empty($fvalue) && in_array($header, array('to', 'cc', 'bcc'))) {
$to_addresses = $RCMAIL->imap->decode_address_list($fvalue, null, $decode_header);
$fvalue = array();
foreach ($to_addresses as $addr_part) {
if (empty($addr_part['mailto']))
continue;
$mailto = mb_strtolower(rcube_idn_to_utf8($addr_part['mailto']));
if (!in_array($mailto, $a_recipients)
&& ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email'])
) {
if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name'])
$string = format_email_recipient($mailto, $addr_part['name']);
else
$string = $mailto;
$fvalue[] = $string;
$a_recipients[] = $addr_part['mailto'];
}
}
$fvalue = implode($separator, $fvalue);
}
$MESSAGE->compose[$header] = $fvalue;
}
unset($a_recipients);
// process $MESSAGE body/attachments, set $MESSAGE_BODY/$HTML_MODE vars and some session data
$MESSAGE_BODY = rcmail_prepare_message_body();
/****** compose mode functions ********/
function rcmail_compose_headers($attrib)
{
global $MESSAGE;
list($form_start, $form_end) = get_form_tags($attrib);
$out = '';
$part = strtolower($attrib['part']);
switch ($part)
{
case 'from':
return $form_start . rcmail_compose_header_from($attrib);
case 'to':
case 'cc':
case 'bcc':
$fname = '_' . $part;
$header = $param = $part;
$allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'tabindex');
$field_type = 'html_textarea';
break;
case 'replyto':
case 'reply-to':
$fname = '_replyto';
$param = 'replyto';
$header = 'reply-to';
case 'followupto':
case 'followup-to':
if (!$fname) {
$fname = '_followupto';
$param = 'followupto';
$header = 'mail-followup-to';
}
$allow_attrib = array('id', 'class', 'style', 'size', 'tabindex');
$field_type = 'html_inputfield';
break;
}
if ($fname && $field_type)
{
// pass the following attributes to the form class
$field_attrib = array('name' => $fname, 'spellcheck' => 'false');
foreach ($attrib as $attr => $value)
if (in_array($attr, $allow_attrib))
$field_attrib[$attr] = $value;
// create teaxtarea object
$input = new $field_type($field_attrib);
$out = $input->show($MESSAGE->compose[$param]);
}
if ($form_start)
$out = $form_start.$out;
// configure autocompletion
rcube_autocomplete_init();
return $out;
}
function rcmail_compose_header_from($attrib)
{
global $MESSAGE, $OUTPUT;
// pass the following attributes to the form class
$field_attrib = array('name' => '_from');
foreach ($attrib as $attr => $value)
if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
$field_attrib[$attr] = $value;
if (count($MESSAGE->identities))
{
$a_signatures = array();
$field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)";
$select_from = new html_select($field_attrib);
// create SELECT element
foreach ($MESSAGE->identities as $sql_arr)
{
$identity_id = $sql_arr['identity_id'];
$select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
// add signature to array
if (!empty($sql_arr['signature']) && empty($COMPOSE['param']['nosig']))
{
$a_signatures[$identity_id]['text'] = $sql_arr['signature'];
$a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false;
if ($a_signatures[$identity_id]['is_html'])
{
$h2t = new html2text($a_signatures[$identity_id]['text'], false, false);
$a_signatures[$identity_id]['plain_text'] = trim($h2t->get_text());
}
}
}
$out = $select_from->show($MESSAGE->compose['from']);
// add signatures to client
$OUTPUT->set_env('signatures', $a_signatures);
}
// no identities, display text input field
else {
$field_attrib['class'] = 'from_address';
$input_from = new html_inputfield($field_attrib);
$out = $input_from->show($MESSAGE->compose['from']);
}
return $out;
}
function rcmail_compose_editor_mode()
{
global $RCMAIL, $MESSAGE, $compose_mode;
static $useHtml;
if ($useHtml !== null)
return $useHtml;
$html_editor = intval($RCMAIL->config->get('htmleditor'));
if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
$useHtml = $MESSAGE->has_html_part();
}
else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
$useHtml = ($html_editor == 1 || ($html_editor == 2 && $MESSAGE->has_html_part()));
}
else { // RCUBE_COMPOSE_FORWARD or NEW
$useHtml = ($html_editor == 1);
}
return $useHtml;
}
function rcmail_prepare_message_body()
{
global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $LINE_LENGTH, $HTML_MODE;
// use posted message body
if (!empty($_POST['_message'])) {
$body = get_input_value('_message', RCUBE_INPUT_POST, true);
$isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST);
}
else if ($COMPOSE['param']['body']) {
$body = $COMPOSE['param']['body'];
$isHtml = false;
}
// forward as attachment
else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $MESSAGE->forward_attachment) {
$isHtml = rcmail_compose_editor_mode();
$body = '';
if (empty($COMPOSE['attachments']))
rcmail_write_forward_attachment($MESSAGE);
}
// reply/edit/draft/forward
else if ($compose_mode) {
$has_html_part = $MESSAGE->has_html_part();
$isHtml = rcmail_compose_editor_mode();
if ($isHtml) {
if ($has_html_part) {
$body = $MESSAGE->first_html_part();
}
else {
$body = $MESSAGE->first_text_part();
// try to remove the signature
if ($RCMAIL->config->get('strip_existing_sig', true))
$body = rcmail_remove_signature($body);
// add HTML formatting
$body = rcmail_plain_body($body);
if ($body)
$body = '<pre>' . $body . '</pre>';
}
}
else {
if ($has_html_part) {
// use html part if it has been used for message (pre)viewing
// decrease line length for quoting
$len = $compose_mode == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH;
$txt = new html2text($MESSAGE->first_html_part(), false, true, $len);
$body = $txt->get_text();
}
else {
$body = $MESSAGE->first_text_part($part);
if ($body && $part && $part->ctype_secondary == 'plain'
&& $part->ctype_parameters['format'] == 'flowed'
) {
$body = rcube_message::unfold_flowed($body);
}
}
}
// compose reply-body
if ($compose_mode == RCUBE_COMPOSE_REPLY)
$body = rcmail_create_reply_body($body, $isHtml);
// forward message body inline
else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
$body = rcmail_create_forward_body($body, $isHtml);
// load draft message body
else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT)
$body = rcmail_create_draft_body($body, $isHtml);
}
else { // new message
$isHtml = rcmail_compose_editor_mode();
}
$plugin = $RCMAIL->plugins->exec_hook('message_compose_body',
array('body' => $body, 'html' => $isHtml, 'mode' => $compose_mode));
$body = $plugin['body'];
unset($plugin);
// add blocked.gif attachment (#1486516)
if ($isHtml && preg_match('#<img src="\./program/blocked\.gif"#', $body)) {
if ($attachment = rcmail_save_image('program/blocked.gif', 'image/gif')) {
$COMPOSE['attachments'][$attachment['id']] = $attachment;
$body = preg_replace('#\./program/blocked\.gif#',
$RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$COMPOSE['id'],
$body);
}
}
$HTML_MODE = $isHtml;
return $body;
}
function rcmail_compose_body($attrib)
{
global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode, $LINE_LENGTH, $HTML_MODE, $MESSAGE_BODY;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
if (empty($attrib['id']))
$attrib['id'] = 'rcmComposeBody';
$attrib['name'] = '_message';
$isHtml = $HTML_MODE;
$out = $form_start ? "$form_start\n" : '';
$saveid = new html_hiddenfield(array('name' => '_draft_saveid', 'value' => $compose_mode==RCUBE_COMPOSE_DRAFT ? str_replace(array('<','>'), "", $MESSAGE->headers->messageID) : ''));
$out .= $saveid->show();
$drafttoggle = new html_hiddenfield(array('name' => '_draft', 'value' => 'yes'));
$out .= $drafttoggle->show();
$msgtype = new html_hiddenfield(array('name' => '_is_html', 'value' => ($isHtml?"1":"0")));
$out .= $msgtype->show();
// If desired, set this textarea to be editable by TinyMCE
if ($isHtml) {
$attrib['class'] = 'mce_editor';
$textarea = new html_textarea($attrib);
$out .= $textarea->show($MESSAGE_BODY);
}
else {
$textarea = new html_textarea($attrib);
$out .= $textarea->show('');
// quote plain text, inject into textarea
$table = get_html_translation_table(HTML_SPECIALCHARS);
$MESSAGE_BODY = strtr($MESSAGE_BODY, $table);
$out = substr($out, 0, -11) . $MESSAGE_BODY . '</textarea>';
}
$out .= $form_end ? "\n$form_end" : '';
$OUTPUT->set_env('composebody', $attrib['id']);
// include HTML editor
rcube_html_editor();
// include GoogieSpell
if (!empty($CONFIG['enable_spellcheck'])) {
$engine = $RCMAIL->config->get('spellcheck_engine','googie');
$dictionary = (bool) $RCMAIL->config->get('spellcheck_dictionary');
$spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages',
array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español',
'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski',
'pt'=>'Português', 'fi'=>'Suomi', 'sv'=>'Svenska'));
// googie works only with two-letter codes
if ($engine == 'googie') {
$lang = strtolower(substr($_SESSION['language'], 0, 2));
$spellcheck_langs_googie = array();
foreach ($spellcheck_langs as $key => $name)
$spellcheck_langs_googie[strtolower(substr($key,0,2))] = $name;
$spellcheck_langs = $spellcheck_langs_googie;
}
else {
$lang = $_SESSION['language'];
// if not found in the list, try with two-letter code
if (!$spellcheck_langs[$lang])
$lang = strtolower(substr($lang, 0, 2));
}
if (!$spellcheck_langs[$lang])
$lang = 'en';
$editor_lang_set = array();
foreach ($spellcheck_langs as $key => $name) {
$editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key);
}
$OUTPUT->include_script('googiespell.js');
$OUTPUT->add_script(sprintf(
"var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=', %s);\n".
"googie.lang_chck_spell = \"%s\";\n".
"googie.lang_rsm_edt = \"%s\";\n".
"googie.lang_close = \"%s\";\n".
"googie.lang_revert = \"%s\";\n".
"googie.lang_no_error_found = \"%s\";\n".
"googie.lang_learn_word = \"%s\";\n".
"googie.setLanguages(%s);\n".
"googie.setCurrentLanguage('%s');\n".
"googie.setSpellContainer('spellcheck-control');\n".
"googie.decorateTextarea('%s');\n".
"%s.set_env('spellcheck', googie);",
!empty($dictionary) ? 'true' : 'false',
JQ(Q(rcube_label('checkspelling'))),
JQ(Q(rcube_label('resumeediting'))),
JQ(Q(rcube_label('close'))),
JQ(Q(rcube_label('revertto'))),
JQ(Q(rcube_label('nospellerrors'))),
JQ(Q(rcube_label('addtodict'))),
json_serialize($spellcheck_langs),
$lang,
$attrib['id'],
JS_OBJECT_NAME), 'foot');
$OUTPUT->add_label('checking');
$OUTPUT->set_env('spellcheck_langs', join(',', $editor_lang_set));
}
$out .= "\n".'<iframe name="savetarget" src="program/blank.gif" style="width:0;height:0;border:none;visibility:hidden;"></iframe>';
return $out;
}
function rcmail_create_reply_body($body, $bodyIsHtml)
{
global $RCMAIL, $MESSAGE, $LINE_LENGTH;
// build reply prefix
$from = array_pop($RCMAIL->imap->decode_address_list($MESSAGE->get_header('from'), 1, false));
$prefix = rcube_label(array(
'name' => 'mailreplyintro',
'vars' => array(
'date' => format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')),
'sender' => $from['name'] ? $from['name'] : rcube_idn_to_utf8($from['mailto']),
)
));
if (!$bodyIsHtml) {
$body = preg_replace('/\r?\n/', "\n", $body);
// try to remove the signature
if ($RCMAIL->config->get('strip_existing_sig', true))
$body = rcmail_remove_signature($body);
// soft-wrap and quote message text
$body = rcmail_wrap_and_quote(rtrim($body, "\n"), $LINE_LENGTH);
$prefix .= "\n";
$suffix = '';
if ($RCMAIL->config->get('top_posting'))
$prefix = "\n\n\n" . $prefix;
}
else {
// save inline images to files
$cid_map = rcmail_write_inline_attachments($MESSAGE);
// set is_safe flag (we need this for html body washing)
rcmail_check_safe($MESSAGE);
// clean up html tags
$body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
// build reply (quote content)
$prefix = '<p>' . Q($prefix) . "</p>\n";
$prefix .= '<blockquote>';
if ($RCMAIL->config->get('top_posting')) {
$prefix = '<br>' . $prefix;
$suffix = '</blockquote>';
}
else {
$suffix = '</blockquote><p></p>';
}
}
return $prefix.$body.$suffix;
}
function rcmail_create_forward_body($body, $bodyIsHtml)
{
global $RCMAIL, $MESSAGE, $COMPOSE;
// add attachments
if (!isset($COMPOSE['forward_attachments']) && is_array($MESSAGE->mime_parts))
$cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
$date = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long'));
$charset = $RCMAIL->output->get_charset();
if (!$bodyIsHtml)
{
$prefix = "\n\n\n-------- " . rcube_label('originalmessage') . " --------\n";
$prefix .= rcube_label('subject') . ': ' . $MESSAGE->subject . "\n";
$prefix .= rcube_label('date') . ': ' . $date . "\n";
$prefix .= rcube_label('from') . ': ' . $MESSAGE->get_header('from') . "\n";
$prefix .= rcube_label('to') . ': ' . $MESSAGE->get_header('to') . "\n";
if ($MESSAGE->headers->cc)
$prefix .= rcube_label('cc') . ': ' . $MESSAGE->get_header('cc') . "\n";
if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
$prefix .= rcube_label('replyto') . ': ' . $MESSAGE->get_header('replyto') . "\n";
$prefix .= "\n";
}
else
{
// set is_safe flag (we need this for html body washing)
rcmail_check_safe($MESSAGE);
// clean up html tags
$body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
$prefix = sprintf(
"<br /><p>-------- " . rcube_label('originalmessage') . " --------</p>" .
"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
"<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
rcube_label('subject'), Q($MESSAGE->subject),
rcube_label('date'), Q($date),
rcube_label('from'), htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $charset),
rcube_label('to'), htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $charset));
if ($MESSAGE->headers->cc)
$prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
rcube_label('cc'),
htmlspecialchars(Q($MESSAGE->get_header('cc'), 'replace'), ENT_COMPAT, $charset));
if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
$prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
rcube_label('replyto'),
htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $charset));
$prefix .= "</tbody></table><br>";
}
return $prefix.$body;
}
function rcmail_create_draft_body($body, $bodyIsHtml)
{
global $MESSAGE, $OUTPUT, $COMPOSE;
/**
* add attachments
* sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
*/
if (empty($COMPOSE['forward_attachments'])
&& is_array($MESSAGE->mime_parts)
&& count($MESSAGE->mime_parts) > 0)
{
$cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
// replace cid with href in inline images links
if ($cid_map)
$body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
}
return $body;
}
function rcmail_remove_signature($body)
{
global $RCMAIL;
$len = strlen($body);
$sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15);
while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
if ($sp == 0 || $body[$sp-1] == "\n") {
// do not touch blocks with more that X lines
if (substr_count($body, "\n", $sp) < $sig_max_lines) {
$body = substr($body, 0, max(0, $sp-1));
}
break;
}
}
return $body;
}
function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
{
global $RCMAIL, $COMPOSE;
$cid_map = $messages = array();
foreach ((array)$message->mime_parts as $pid => $part)
{
if (($part->ctype_primary != 'message' || !$bodyIsHtml) && $part->ctype_primary != 'multipart' &&
($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename)
&& $part->mimetype != 'application/ms-tnef'
) {
$skip = false;
if ($part->mimetype == 'message/rfc822') {
$messages[] = $part->mime_id;
} else if ($messages) {
// skip attachments included in message/rfc822 attachment (#1486487)
foreach ($messages as $mimeid)
if (strpos($part->mime_id, $mimeid.'.') === 0) {
$skip = true;
break;
}
}
if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) {
$COMPOSE['attachments'][$attachment['id']] = $attachment;
if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
$url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$COMPOSE['id'];
if ($part->content_id)
$cid_map['cid:'.$part->content_id] = $url;
else
$cid_map[$part->content_location] = $url;
}
}
}
}
$COMPOSE['forward_attachments'] = true;
return $cid_map;
}
function rcmail_write_inline_attachments(&$message)
{
global $RCMAIL, $COMPOSE;
$cid_map = array();
foreach ((array)$message->mime_parts as $pid => $part) {
if (($part->content_id || $part->content_location) && $part->filename) {
if ($attachment = rcmail_save_attachment($message, $pid)) {
$COMPOSE['attachments'][$attachment['id']] = $attachment;
$url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$COMPOSE['id'];
if ($part->content_id)
$cid_map['cid:'.$part->content_id] = $url;
else
$cid_map[$part->content_location] = $url;
}
}
}
return $cid_map;
}
// Creates an attachment from the forwarded message
function rcmail_write_forward_attachment(&$message)
{
global $RCMAIL, $COMPOSE;
if (strlen($message->subject)) {
$name = mb_substr($message->subject, 0, 64) . '.eml';
}
else {
$name = 'message_rfc822.eml';
}
$mem_limit = parse_bytes(ini_get('memory_limit'));
$curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
$data = $path = null;
// don't load too big attachments into memory
if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) {
$temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
$path = tempnam($temp_dir, 'rcmAttmnt');
if ($fp = fopen($path, 'w')) {
$RCMAIL->imap->get_raw_body($message->uid, $fp);
fclose($fp);
} else
return false;
} else {
$data = $RCMAIL->imap->get_raw_body($message->uid);
}
$attachment = array(
'group' => $COMPOSE['id'],
'name' => $name,
'mimetype' => 'message/rfc822',
'data' => $data,
'path' => $path,
'size' => $path ? filesize($path) : strlen($data),
);
$attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment);
if ($attachment['status']) {
unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
$COMPOSE['attachments'][$attachment['id']] = $attachment;
return true;
} else if ($path) {
@unlink($path);
}
return false;
}
function rcmail_save_attachment(&$message, $pid)
{
global $COMPOSE;
$rcmail = rcmail::get_instance();
$part = $message->mime_parts[$pid];
$mem_limit = parse_bytes(ini_get('memory_limit'));
$curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
$data = $path = null;
// don't load too big attachments into memory
if ($mem_limit > 0 && $part->size > $mem_limit - $curr_mem) {
$temp_dir = unslashify($rcmail->config->get('temp_dir'));
$path = tempnam($temp_dir, 'rcmAttmnt');
if ($fp = fopen($path, 'w')) {
$message->get_part_content($pid, $fp);
fclose($fp);
} else
return false;
} else {
$data = $message->get_part_content($pid);
}
$attachment = array(
'group' => $COMPOSE['id'],
'name' => $part->filename ? $part->filename : 'Part_'.$pid.'.'.$part->ctype_secondary,
'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
'content_id' => $part->content_id,
'data' => $data,
'path' => $path,
'size' => $path ? filesize($path) : strlen($data),
);
$attachment = $rcmail->plugins->exec_hook('attachment_save', $attachment);
if ($attachment['status']) {
unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
return $attachment;
} else if ($path) {
@unlink($path);
}
return false;
}
function rcmail_save_image($path, $mimetype='')
{
global $COMPOSE;
// handle attachments in memory
$data = file_get_contents($path);
$attachment = array(
'group' => $COMPOSE['id'],
'name' => rcmail_basename($path),
'mimetype' => $mimetype ? $mimetype : rc_mime_content_type($path, $name),
'data' => $data,
'size' => strlen($data),
);
$attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
if ($attachment['status']) {
unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
return $attachment;
}
return false;
}
function rcmail_basename($filename)
{
// basename() is not unicode safe and locale dependent
if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) {
return preg_replace('/^.*[\\\\\\/]/', '', $filename);
} else {
return preg_replace('/^.*[\/]/', '', $filename);
}
}
function rcmail_compose_subject($attrib)
{
global $MESSAGE, $COMPOSE, $compose_mode;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
$attrib['name'] = '_subject';
$attrib['spellcheck'] = 'true';
$textfield = new html_inputfield($attrib);
$subject = '';
// use subject from post
if (isset($_POST['_subject'])) {
$subject = get_input_value('_subject', RCUBE_INPUT_POST, TRUE);
}
// create a reply-subject
else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
if (preg_match('/^re:/i', $MESSAGE->subject))
$subject = $MESSAGE->subject;
else
$subject = 'Re: '.$MESSAGE->subject;
}
// create a forward-subject
else if ($compose_mode == RCUBE_COMPOSE_FORWARD) {
if (preg_match('/^fwd:/i', $MESSAGE->subject))
$subject = $MESSAGE->subject;
else
$subject = 'Fwd: '.$MESSAGE->subject;
}
// creeate a draft-subject
else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
$subject = $MESSAGE->subject;
}
else if (!empty($COMPOSE['param']['subject'])) {
$subject = $COMPOSE['param']['subject'];
}
$out = $form_start ? "$form_start\n" : '';
$out .= $textfield->show($subject);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_compose_attachment_list($attrib)
{
global $OUTPUT, $CONFIG, $COMPOSE;
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmAttachmentList';
$out = "\n";
$jslist = array();
if (is_array($COMPOSE['attachments'])) {
if ($attrib['deleteicon']) {
$button = html::img(array(
'src' => $CONFIG['skin_path'] . $attrib['deleteicon'],
'alt' => rcube_label('delete')
));
}
else
$button = Q(rcube_label('delete'));
foreach ($COMPOSE['attachments'] as $id => $a_prop)
{
if (empty($a_prop))
continue;
$out .= html::tag('li', array('id' => 'rcmfile'.$id),
html::a(array(
'href' => "#delete",
'title' => rcube_label('delete'),
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id)),
$button) . Q($a_prop['name']));
$jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']);
}
}
if ($attrib['deleteicon'])
$COMPOSE['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon'];
if ($attrib['cancelicon'])
$OUTPUT->set_env('cancelicon', $CONFIG['skin_path'] . $attrib['cancelicon']);
if ($attrib['loadingicon'])
$OUTPUT->set_env('loadingicon', $CONFIG['skin_path'] . $attrib['loadingicon']);
$OUTPUT->set_env('attachments', $jslist);
$OUTPUT->add_gui_object('attachmentlist', $attrib['id']);
return html::tag('ul', $attrib, $out, html::$common_attrib);
}
function rcmail_compose_attachment_form($attrib)
{
global $RCMAIL, $OUTPUT;
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmUploadbox';
// Get filesize, enable upload progress bar
$max_filesize = rcube_upload_init();
$button = new html_inputfield(array('type' => 'button'));
$out = html::div($attrib,
$OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'),
html::div(null, rcmail_compose_attachment_field(array('size' => $attrib['attachmentfieldsize']))) .
html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) .
html::div('buttons',
$button->show(rcube_label('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' .
$button->show(rcube_label('upload'), array('class' => 'button mainaction', 'onclick' => JS_OBJECT_NAME . ".command('send-attachment', this.form)"))
)
)
);
$OUTPUT->add_gui_object('uploadbox', $attrib['id']);
return $out;
}
function rcmail_compose_attachment_field($attrib)
{
$attrib['type'] = 'file';
$attrib['name'] = '_attachments[]';
$attrib['multiple'] = 'multiple';
$field = new html_inputfield($attrib);
return $field->show();
}
function rcmail_priority_selector($attrib)
{
global $MESSAGE;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
$attrib['name'] = '_priority';
$selector = new html_select($attrib);
$selector->add(array(rcube_label('lowest'),
rcube_label('low'),
rcube_label('normal'),
rcube_label('high'),
rcube_label('highest')),
array(5, 4, 0, 2, 1));
if (isset($_POST['_priority']))
$sel = $_POST['_priority'];
else if (intval($MESSAGE->headers->priority) != 3)
$sel = intval($MESSAGE->headers->priority);
else
$sel = 0;
$out = $form_start ? "$form_start\n" : '';
$out .= $selector->show($sel);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_receipt_checkbox($attrib)
{
global $RCMAIL, $MESSAGE, $compose_mode;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
if (!isset($attrib['id']))
$attrib['id'] = 'receipt';
$attrib['name'] = '_receipt';
$attrib['value'] = '1';
$checkbox = new html_checkbox($attrib);
if ($MESSAGE && in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
$mdn_default = (bool) $MESSAGE->headers->mdn_to;
else
$mdn_default = $RCMAIL->config->get('mdn_default');
$out = $form_start ? "$form_start\n" : '';
$out .= $checkbox->show($mdn_default);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_dsn_checkbox($attrib)
{
global $RCMAIL;
list($form_start, $form_end) = get_form_tags($attrib);
unset($attrib['form']);
if (!isset($attrib['id']))
$attrib['id'] = 'dsn';
$attrib['name'] = '_dsn';
$attrib['value'] = '1';
$checkbox = new html_checkbox($attrib);
$out = $form_start ? "$form_start\n" : '';
$out .= $checkbox->show($RCMAIL->config->get('dsn_default'));
$out .= $form_end ? "\n$form_end" : '';
return $out;
}
function rcmail_editor_selector($attrib)
{
global $CONFIG, $MESSAGE, $compose_mode;
// determine whether HTML or plain text should be checked
$useHtml = rcmail_compose_editor_mode();
if (empty($attrib['editorid']))
$attrib['editorid'] = 'rcmComposeBody';
if (empty($attrib['name']))
$attrib['name'] = 'editorSelect';
$attrib['onchange'] = "return rcmail_toggle_editor(this, '".$attrib['editorid']."', '_is_html')";
$select = new html_select($attrib);
$select->add(Q(rcube_label('htmltoggle')), 'html');
$select->add(Q(rcube_label('plaintoggle')), 'plain');
return $select->show($useHtml ? 'html' : 'plain');
foreach ($choices as $value => $text) {
$attrib['id'] = '_' . $value;
$attrib['value'] = $value;
$selector .= $radio->show($chosenvalue, $attrib) . html::label($attrib['id'], Q(rcube_label($text)));
}
return $selector;
}
function rcmail_store_target_selection($attrib)
{
global $COMPOSE;
$attrib['name'] = '_store_target';
$select = rcmail_mailbox_select(array_merge($attrib, array(
'noselection' => '- '.rcube_label('dontsave').' -',
'folder_filter' => 'mail',
'folder_rights' => 'w',
)));
return $select->show($COMPOSE['param']['sent_mbox'], $attrib);
}
function rcmail_check_sent_folder($folder, $create=false)
{
global $RCMAIL;
if ($RCMAIL->imap->mailbox_exists($folder, true)) {
return true;
}
// folder may exist but isn't subscribed (#1485241)
if ($create) {
if (!$RCMAIL->imap->mailbox_exists($folder))
return $RCMAIL->imap->create_mailbox($folder, true);
else
return $RCMAIL->imap->subscribe($folder);
}
return false;
}
function get_form_tags($attrib)
{
global $RCMAIL, $MESSAGE_FORM, $COMPOSE;
$form_start = '';
if (!$MESSAGE_FORM)
{
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
$hiddenfields->add(array('name' => '_action', 'value' => 'send'));
$hiddenfields->add(array('name' => '_id', 'value' => $COMPOSE['id']));
$form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
$form_start .= $hiddenfields->show();
}
$form_end = ($MESSAGE_FORM && !strlen($attrib['form'])) ? '</form>' : '';
$form_name = !empty($attrib['form']) ? $attrib['form'] : 'form';
if (!$MESSAGE_FORM)
$RCMAIL->output->add_gui_object('messageform', $form_name);
$MESSAGE_FORM = $form_name;
return array($form_start, $form_end);
}
// register UI objects
$OUTPUT->add_handlers(array(
'composeheaders' => 'rcmail_compose_headers',
'composesubject' => 'rcmail_compose_subject',
'composebody' => 'rcmail_compose_body',
'composeattachmentlist' => 'rcmail_compose_attachment_list',
'composeattachmentform' => 'rcmail_compose_attachment_form',
'composeattachment' => 'rcmail_compose_attachment_field',
'priorityselector' => 'rcmail_priority_selector',
'editorselector' => 'rcmail_editor_selector',
'receiptcheckbox' => 'rcmail_receipt_checkbox',
'dsncheckbox' => 'rcmail_dsn_checkbox',
'storetarget' => 'rcmail_store_target_selection',
));
$OUTPUT->send('compose');
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index e4e5093b1..9d4d42562 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -1,761 +1,761 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/mail/sendmail.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2011, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Compose a new mail message with all headers and attachments |
| and send it using the PEAR::Net_SMTP class or with PHP mail() |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// remove all scripts and act as called in frame
$OUTPUT->reset();
$OUTPUT->framed = TRUE;
$savedraft = !empty($_POST['_draft']) ? true : false;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
/****** checks ********/
if (!isset($COMPOSE['id'])) {
raise_error(array('code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Invalid compose ID"), true, false);
$OUTPUT->show_message('internalerror', 'error');
$OUTPUT->send('iframe');
}
if (!$savedraft) {
if (empty($_POST['_to']) && empty($_POST['_cc']) && empty($_POST['_bcc'])
&& empty($_POST['_subject']) && $_POST['_message']) {
$OUTPUT->show_message('sendingfailed', 'error');
$OUTPUT->send('iframe');
}
if(!empty($CONFIG['sendmail_delay'])) {
$wait_sec = time() - intval($CONFIG['sendmail_delay']) - intval($CONFIG['last_message_time']);
if($wait_sec < 0) {
$OUTPUT->show_message('senttooquickly', 'error', array('sec' => $wait_sec * -1));
$OUTPUT->send('iframe');
}
}
}
/****** message sending functions ********/
// encrypt parts of the header
function rcmail_encrypt_header($what)
{
global $CONFIG, $RCMAIL;
if (!$CONFIG['http_received_header_encrypt']) {
return $what;
}
return $RCMAIL->encrypt($what);
}
// get identity record
function rcmail_get_identity($id)
{
- global $USER, $OUTPUT;
+ global $RCMAIL, $OUTPUT;
- if ($sql_arr = $USER->get_identity($id)) {
+ if ($sql_arr = $RCMAIL->user->get_identity($id)) {
$out = $sql_arr;
$out['mailto'] = $sql_arr['email'];
$out['string'] = format_email_recipient($sql_arr['email'],
rcube_charset_convert($sql_arr['name'], RCMAIL_CHARSET, $OUTPUT->get_charset()));
return $out;
}
return FALSE;
}
/**
* go from this:
* <img src="http[s]://.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
*
* to this:
*
* <img src="/path/on/server/.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
* ...
*/
function rcmail_fix_emoticon_paths(&$mime_message)
{
global $CONFIG;
$body = $mime_message->getHTMLBody();
// remove any null-byte characters before parsing
$body = preg_replace('/\x00/', '', $body);
$searchstr = 'program/js/tiny_mce/plugins/emotions/img/';
$offset = 0;
// keep track of added images, so they're only added once
$included_images = array();
if (preg_match_all('# src=[\'"]([^\'"]+)#', $body, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[1] as $m) {
// find emoticon image tags
if (preg_match('#'.$searchstr.'(.*)$#', $m[0], $imatches)) {
$image_name = $imatches[1];
// sanitize image name so resulting attachment doesn't leave images dir
$image_name = preg_replace('/[^a-zA-Z0-9_\.\-]/i', '', $image_name);
$img_file = INSTALL_PATH . '/' . $searchstr . $image_name;
if (! in_array($image_name, $included_images)) {
// add the image to the MIME message
if (! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name))
$OUTPUT->show_message("emoticonerror", 'error');
array_push($included_images, $image_name);
}
$body = substr_replace($body, $img_file, $m[1] + $offset, strlen($m[0]));
$offset += strlen($img_file) - strlen($m[0]);
}
}
}
$mime_message->setHTMLBody($body);
return $body;
}
/**
* Parse and cleanup email address input (and count addresses)
*
* @param string Address input
* @param boolean Do count recipients (saved in global $RECIPIENT_COUNT)
* @param boolean Validate addresses (errors saved in global $EMAIL_FORMAT_ERROR)
* @return string Canonical recipients string separated by comma
*/
function rcmail_email_input_format($mailto, $count=false, $check=true)
{
global $RCMAIL, $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
// simplified email regexp, supporting quoted local part
$email_regexp = '(\S+|("[^"]+"))@\S+';
$delim = trim($RCMAIL->config->get('recipients_separator', ','));
$regexp = array("/[,;$delim]\s*[\r\n]+/", '/[\r\n]+/', "/[,;$delim]\s*\$/m", '/;/', '/(\S{1})(<'.$email_regexp.'>)/U');
$replace = array($delim.' ', ', ', '', $delim, '\\1 \\2');
// replace new lines and strip ending ', ', make address input more valid
$mailto = trim(preg_replace($regexp, $replace, $mailto));
$result = array();
$items = rcube_explode_quoted_string($delim, $mailto);
foreach($items as $item) {
$item = trim($item);
// address in brackets without name (do nothing)
if (preg_match('/^<'.$email_regexp.'>$/', $item)) {
$item = rcube_idn_to_ascii(trim($item, '<>'));
$result[] = '<' . $item . '>';
// address without brackets and without name (add brackets)
} else if (preg_match('/^'.$email_regexp.'$/', $item)) {
$item = rcube_idn_to_ascii($item);
$result[] = '<' . $item . '>';
// address with name (handle name)
} else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) {
$address = $matches[0];
$name = trim(str_replace($address, '', $item), '" ');
$address = rcube_idn_to_ascii(trim($address, '<>'));
$result[] = format_email_recipient($address, $name);
$item = $address;
} else if (trim($item)) {
continue;
}
// check address format
$item = trim($item, '<>');
if ($item && $check && !check_email($item)) {
$EMAIL_FORMAT_ERROR = $item;
return;
}
}
if ($count) {
$RECIPIENT_COUNT += count($result);
}
return implode(', ', $result);
}
/****** compose message ********/
if (strlen($_POST['_draft_saveid']) > 3)
$olddraftmessageid = get_input_value('_draft_saveid', RCUBE_INPUT_POST);
$message_id = rcmail_gen_message_id();
// set default charset
$input_charset = $OUTPUT->get_charset();
$message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $input_charset;
$EMAIL_FORMAT_ERROR = NULL;
$RECIPIENT_COUNT = 0;
$mailto = rcmail_email_input_format(get_input_value('_to', RCUBE_INPUT_POST, TRUE, $message_charset), true);
$mailcc = rcmail_email_input_format(get_input_value('_cc', RCUBE_INPUT_POST, TRUE, $message_charset), true);
$mailbcc = rcmail_email_input_format(get_input_value('_bcc', RCUBE_INPUT_POST, TRUE, $message_charset), true);
if ($EMAIL_FORMAT_ERROR) {
$OUTPUT->show_message('emailformaterror', 'error', array('email' => $EMAIL_FORMAT_ERROR));
$OUTPUT->send('iframe');
}
if (empty($mailto) && !empty($mailcc)) {
$mailto = $mailcc;
$mailcc = null;
}
else if (empty($mailto))
$mailto = 'undisclosed-recipients:;';
// Get sender name and address...
$from = get_input_value('_from', RCUBE_INPUT_POST, true, $message_charset);
// ... from identity...
if (is_numeric($from)) {
if (is_array($identity_arr = rcmail_get_identity($from))) {
if ($identity_arr['mailto'])
$from = $identity_arr['mailto'];
if ($identity_arr['string'])
$from_string = $identity_arr['string'];
}
else {
$from = null;
}
}
// ... if there is no identity record, this might be a custom from
else if ($from_string = rcmail_email_input_format($from)) {
if (preg_match('/(\S+@\S+)/', $from_string, $m))
$from = trim($m[1], '<>');
else
$from = null;
}
if (!$from_string && $from)
$from_string = $from;
// compose headers array
$headers = array();
// if configured, the Received headers goes to top, for good measure
if ($CONFIG['http_received_header'])
{
$nldlm = "\r\n\t";
// FROM/VIA
$http_header = 'from ';
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$host = $_SERVER['HTTP_X_FORWARDED_FOR'];
$hostname = gethostbyaddr($host);
if ($CONFIG['http_received_header_encrypt']) {
$http_header .= rcmail_encrypt_header($hostname);
if ($host != $hostname)
$http_header .= ' ('. rcmail_encrypt_header($host) . ')';
} else {
$http_header .= (($host != $hostname) ? $hostname : '[' . $host . ']');
if ($host != $hostname)
$http_header .= ' (['. $host .'])';
}
$http_header .= $nldlm . ' via ';
}
$host = $_SERVER['REMOTE_ADDR'];
$hostname = gethostbyaddr($host);
if ($CONFIG['http_received_header_encrypt']) {
$http_header .= rcmail_encrypt_header($hostname);
if ($host != $hostname)
$http_header .= ' ('. rcmail_encrypt_header($host) . ')';
} else {
$http_header .= (($host != $hostname) ? $hostname : '[' . $host . ']');
if ($host != $hostname)
$http_header .= ' (['. $host .'])';
}
// BY
$http_header .= $nldlm . 'by ' . $_SERVER['HTTP_HOST'];
// WITH
$http_header .= $nldlm . 'with HTTP (' . $_SERVER['SERVER_PROTOCOL'] .
' '.$_SERVER['REQUEST_METHOD'] . '); ' . date('r');
$http_header = wordwrap($http_header, 69, $nldlm);
$headers['Received'] = $http_header;
}
$headers['Date'] = rcmail_user_date();
$headers['From'] = rcube_charset_convert($from_string, RCMAIL_CHARSET, $message_charset);
$headers['To'] = $mailto;
// additional recipients
if (!empty($mailcc)) {
$headers['Cc'] = $mailcc;
}
if (!empty($mailbcc)) {
$headers['Bcc'] = $mailbcc;
}
if (!empty($identity_arr['bcc'])) {
$headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc'];
$RECIPIENT_COUNT ++;
}
if (($max_recipients = (int) $RCMAIL->config->get('max_recipients')) > 0) {
if ($RECIPIENT_COUNT > $max_recipients) {
$OUTPUT->show_message('toomanyrecipients', 'error', array('max' => $max_recipients));
$OUTPUT->send('iframe');
}
}
// add subject
$headers['Subject'] = trim(get_input_value('_subject', RCUBE_INPUT_POST, TRUE, $message_charset));
if (!empty($identity_arr['organization'])) {
$headers['Organization'] = $identity_arr['organization'];
}
if (!empty($_POST['_replyto'])) {
$headers['Reply-To'] = rcmail_email_input_format(get_input_value('_replyto', RCUBE_INPUT_POST, TRUE, $message_charset));
}
else if (!empty($identity_arr['reply-to'])) {
$headers['Reply-To'] = rcmail_email_input_format($identity_arr['reply-to'], false, true);
}
if (!empty($headers['Reply-To'])) {
$headers['Mail-Reply-To'] = $headers['Reply-To'];
}
if (!empty($_POST['_followupto'])) {
$headers['Mail-Followup-To'] = rcmail_email_input_format(get_input_value('_followupto', RCUBE_INPUT_POST, TRUE, $message_charset));
}
if (!empty($COMPOSE['reply_msgid'])) {
$headers['In-Reply-To'] = $COMPOSE['reply_msgid'];
}
// remember reply/forward UIDs in special headers
if (!empty($COMPOSE['reply_uid']) && $savedraft) {
$headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $COMPOSE['reply_uid']);
}
else if (!empty($COMPOSE['forward_uid']) && $savedraft) {
$headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']);
}
if (!empty($COMPOSE['references'])) {
$headers['References'] = $COMPOSE['references'];
}
if (!empty($_POST['_priority'])) {
$priority = intval($_POST['_priority']);
$a_priorities = array(1=>'highest', 2=>'high', 4=>'low', 5=>'lowest');
if ($str_priority = $a_priorities[$priority]) {
$headers['X-Priority'] = sprintf("%d (%s)", $priority, ucfirst($str_priority));
}
}
if (!empty($_POST['_receipt'])) {
$headers['Return-Receipt-To'] = $from_string;
$headers['Disposition-Notification-To'] = $from_string;
}
// additional headers
$headers['Message-ID'] = $message_id;
$headers['X-Sender'] = $from;
if (is_array($headers['X-Draft-Info'])) {
$headers['X-Draft-Info'] = rcmail_draftinfo_encode($headers['X-Draft-Info'] + array('folder' => $COMPOSE['mailbox']));
}
if (!empty($CONFIG['useragent'])) {
$headers['User-Agent'] = $CONFIG['useragent'];
}
// exec hook for header checking and manipulation
$data = $RCMAIL->plugins->exec_hook('message_outgoing_headers', array('headers' => $headers));
// sending aborted by plugin
if ($data['abort'] && !$savedraft) {
$OUTPUT->show_message($data['message'] ? $data['message'] : 'sendingfailed');
$OUTPUT->send('iframe');
}
else
$headers = $data['headers'];
$isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST);
// fetch message body
$message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset);
if ($isHtml) {
$font = rcube_fontdefs($RCMAIL->config->get('default_font', 'Verdana'));
$bstyle = $font && is_string($font) ? " style='font-family: $font'" : '';
// append doctype and html/body wrappers
$message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' .
"\r\n<html><body$bstyle>\r\n" . $message_body;
}
if (!$savedraft) {
if ($isHtml) {
// remove signature's div ID
$message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body);
// add inline css for blockquotes
$bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%';
$message_body = preg_replace('/<blockquote>/',
'<blockquote type="cite" style="'.$bstyle.'">', $message_body);
}
// Check spelling before send
if ($CONFIG['spellcheck_before_send'] && $CONFIG['enable_spellcheck']
&& empty($COMPOSE['spell_checked']) && !empty($message_body)
) {
$spellchecker = new rcube_spellchecker(get_input_value('_lang', RCUBE_INPUT_GPC));
$spell_result = $spellchecker->check($message_body, $isHtml);
$COMPOSE['spell_checked'] = true;
if (!$spell_result) {
$result = $isHtml ? $spellchecker->get_words() : $spellchecker->get_xml();
$OUTPUT->show_message('mispellingsfound', 'error');
$OUTPUT->command('spellcheck_resume', $isHtml, $result);
$OUTPUT->send('iframe');
}
}
// generic footer for all messages
if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) {
$footer = file_get_contents(realpath($CONFIG['generic_message_footer_html']));
$footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
}
else if (!empty($CONFIG['generic_message_footer'])) {
$footer = file_get_contents(realpath($CONFIG['generic_message_footer']));
$footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
if ($isHtml)
$footer = '<pre>'.$footer.'</pre>';
}
if ($footer)
$message_body .= "\r\n" . $footer;
}
if ($isHtml) {
$message_body .= "\r\n</body></html>\r\n";
}
// set line length for body wrapping
$LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
// Since we can handle big messages with disk usage, we need more time to work
@set_time_limit(0);
// create PEAR::Mail_mime instance
$MAIL_MIME = new Mail_mime("\r\n");
// Check if we have enough memory to handle the message in it
// It's faster than using files, so we'll do this if we only can
if (is_array($COMPOSE['attachments']) && $CONFIG['smtp_server']
&& ($mem_limit = parse_bytes(ini_get('memory_limit'))))
{
$memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
foreach ($COMPOSE['attachments'] as $id => $attachment)
$memory += $attachment['size'];
// Yeah, Net_SMTP needs up to 12x more memory, 1.33 is for base64
if ($memory * 1.33 * 12 > $mem_limit)
$MAIL_MIME->setParam('delay_file_io', true);
}
// For HTML-formatted messages, construct the MIME message with both
// the HTML part and the plain-text part
if ($isHtml) {
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $message_body, 'type' => 'html', 'message' => $MAIL_MIME));
$MAIL_MIME->setHTMLBody($plugin['body']);
// replace emoticons
$plugin['body'] = rcmail_replace_emoticons($plugin['body']);
// add a plain text version of the e-mail as an alternative part.
$h2t = new html2text($plugin['body'], false, true, 0);
$plainTextPart = rc_wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n");
$plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true);
if (!$plainTextPart) {
// empty message body breaks attachment handling in drafts
$plainTextPart = "\r\n";
}
else {
// make sure all line endings are CRLF (#1486712)
$plainTextPart = preg_replace('/\r?\n/', "\r\n", $plainTextPart);
}
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $plainTextPart, 'type' => 'alternative', 'message' => $MAIL_MIME));
$MAIL_MIME->setTXTBody($plugin['body']);
// look for "emoticon" images from TinyMCE and change their src paths to
// be file paths on the server instead of URL paths.
$message_body = rcmail_fix_emoticon_paths($MAIL_MIME);
}
else {
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $message_body, 'type' => 'plain', 'message' => $MAIL_MIME));
$message_body = $plugin['body'];
// compose format=flowed content if enabled
if ($flowed = $RCMAIL->config->get('send_format_flowed', true))
$message_body = rcube_message::format_flowed($message_body, min($LINE_LENGTH+2, 79));
else
$message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n");
$message_body = wordwrap($message_body, 998, "\r\n", true);
if (!strlen($message_body)) {
// empty message body breaks attachment handling in drafts
$message_body = "\r\n";
}
$MAIL_MIME->setTXTBody($message_body, false, true);
}
// add stored attachments, if any
if (is_array($COMPOSE['attachments']))
{
foreach ($COMPOSE['attachments'] as $id => $attachment) {
// This hook retrieves the attachment contents from the file storage backend
$attachment = $RCMAIL->plugins->exec_hook('attachment_get', $attachment);
$dispurl = '/\ssrc\s*=\s*[\'"]*\S+display-attachment\S+file=rcmfile' . preg_quote($attachment['id']) . '[\s\'"]*/';
$message_body = $MAIL_MIME->getHTMLBody();
if ($isHtml && (preg_match($dispurl, $message_body) > 0)) {
$message_body = preg_replace($dispurl, ' src="'.$attachment['name'].'" ', $message_body);
$MAIL_MIME->setHTMLBody($message_body);
if ($attachment['data'])
$MAIL_MIME->addHTMLImage($attachment['data'], $attachment['mimetype'], $attachment['name'], false);
else
$MAIL_MIME->addHTMLImage($attachment['path'], $attachment['mimetype'], $attachment['name'], true);
}
else {
$ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914
$file = $attachment['data'] ? $attachment['data'] : $attachment['path'];
// .eml attachments send inline
$MAIL_MIME->addAttachment($file,
$ctype,
$attachment['name'],
($attachment['data'] ? false : true),
($ctype == 'message/rfc822' ? '8bit' : 'base64'),
($ctype == 'message/rfc822' ? 'inline' : 'attachment'),
'', '', '',
$CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
$CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL,
'', RCMAIL_CHARSET
);
}
}
}
// choose transfer encoding for plain/text body
if (preg_match('/[^\x00-\x7F]/', $MAIL_MIME->getTXTBody()))
$transfer_encoding = $RCMAIL->config->get('force_7bit') ? 'quoted-printable' : '8bit';
else
$transfer_encoding = '7bit';
// encoding settings for mail composing
$MAIL_MIME->setParam('text_encoding', $transfer_encoding);
$MAIL_MIME->setParam('html_encoding', 'quoted-printable');
$MAIL_MIME->setParam('head_encoding', 'quoted-printable');
$MAIL_MIME->setParam('head_charset', $message_charset);
$MAIL_MIME->setParam('html_charset', $message_charset);
$MAIL_MIME->setParam('text_charset', $message_charset . ($flowed ? ";\r\n format=flowed" : ''));
// encoding subject header with mb_encode provides better results with asian characters
if (function_exists('mb_encode_mimeheader')) {
mb_internal_encoding($message_charset);
$headers['Subject'] = mb_encode_mimeheader($headers['Subject'],
$message_charset, 'Q', "\r\n", 8);
mb_internal_encoding(RCMAIL_CHARSET);
}
// pass headers to message object
$MAIL_MIME->headers($headers);
// Begin SMTP Delivery Block
if (!$savedraft)
{
// check 'From' address (identity may be incomplete)
if (empty($from)) {
$OUTPUT->show_message('nofromaddress', 'error');
$OUTPUT->send('iframe');
}
// Handle Delivery Status Notification request
if (!empty($_POST['_dsn'])) {
$smtp_opts['dsn'] = true;
}
$sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto,
$smtp_error, $mailbody_file, $smtp_opts);
// return to compose page if sending failed
if (!$sent)
{
// remove temp file
if ($mailbody_file) {
unlink($mailbody_file);
}
if ($smtp_error)
$OUTPUT->show_message($smtp_error['label'], 'error', $smtp_error['vars']);
else
$OUTPUT->show_message('sendingfailed', 'error');
$OUTPUT->send('iframe');
}
// save message sent time
if (!empty($CONFIG['sendmail_delay']))
$RCMAIL->user->save_prefs(array('last_message_time' => time()));
// set replied/forwarded flag
if ($COMPOSE['reply_uid'])
$RCMAIL->imap->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']);
else if ($COMPOSE['forward_uid'])
$RCMAIL->imap->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']);
} // End of SMTP Delivery Block
// Determine which folder to save message
if ($savedraft)
$store_target = $CONFIG['drafts_mbox'];
else
$store_target = isset($_POST['_store_target']) ? get_input_value('_store_target', RCUBE_INPUT_POST) : $CONFIG['sent_mbox'];
if ($store_target) {
// check if folder is subscribed
if ($RCMAIL->imap->mailbox_exists($store_target, true))
$store_folder = true;
// folder may be existing but not subscribed (#1485241)
else if (!$RCMAIL->imap->mailbox_exists($store_target))
$store_folder = $RCMAIL->imap->create_mailbox($store_target, true);
else if ($RCMAIL->imap->subscribe($store_target))
$store_folder = true;
// append message to sent box
if ($store_folder) {
// message body in file
if ($mailbody_file || $MAIL_MIME->getParam('delay_file_io')) {
$headers = $MAIL_MIME->txtHeaders();
// file already created
if ($mailbody_file)
$msg = $mailbody_file;
else {
$temp_dir = $RCMAIL->config->get('temp_dir');
$mailbody_file = tempnam($temp_dir, 'rcmMsg');
if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file)))
$msg = $mailbody_file;
}
}
else {
$msg = $MAIL_MIME->getMessage();
$headers = '';
}
if (PEAR::isError($msg))
raise_error(array('code' => 650, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not create message: ".$msg->getMessage()),
TRUE, FALSE);
else {
$saved = $RCMAIL->imap->save_message($store_target, $msg, $headers, $mailbody_file ? true : false);
}
if ($mailbody_file) {
unlink($mailbody_file);
$mailbody_file = null;
}
// raise error if saving failed
if (!$saved) {
raise_error(array('code' => 800, 'type' => 'imap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not save message in $store_target"), TRUE, FALSE);
if ($savedraft) {
$OUTPUT->show_message('errorsaving', 'error');
$OUTPUT->send('iframe');
}
}
}
if ($olddraftmessageid) {
// delete previous saved draft
// @TODO: use message UID (remember to check UIDVALIDITY) to skip this SEARCH
$delete_idx = $RCMAIL->imap->search_once($CONFIG['drafts_mbox'],
'HEADER Message-ID '.$olddraftmessageid);
if ($del_uid = $delete_idx->getElement('FIRST')) {
$deleted = $RCMAIL->imap->delete_message($del_uid, $CONFIG['drafts_mbox']);
// raise error if deletion of old draft failed
if (!$deleted)
raise_error(array('code' => 800, 'type' => 'imap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
}
}
}
// remove temp file
else if ($mailbody_file) {
unlink($mailbody_file);
}
if ($savedraft) {
$msgid = strtr($message_id, array('>' => '', '<' => ''));
// remember new draft-uid ($saved could be an UID or TRUE here)
if (is_bool($saved)) {
$draft_idx = $RCMAIL->imap->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid);
$saved = $draft_idx->getElement('FIRST');
}
$COMPOSE['param']['draft_uid'] = $saved;
// display success
$OUTPUT->show_message('messagesaved', 'confirmation');
// update "_draft_saveid" and the "cmp_hash" to prevent "Unsaved changes" warning
$OUTPUT->command('set_draft_id', $msgid);
$OUTPUT->command('compose_field_hash', true);
// start the auto-save timer again
$OUTPUT->command('auto_save_start');
$OUTPUT->send('iframe');
}
else {
rcmail_compose_cleanup($COMPOSE_ID);
if ($store_folder && !$saved)
$OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'));
else
$OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'));
$OUTPUT->send('iframe');
}
diff --git a/program/steps/settings/delete_identity.inc b/program/steps/settings/delete_identity.inc
index 275bcb684..a110c1c6a 100644
--- a/program/steps/settings/delete_identity.inc
+++ b/program/steps/settings/delete_identity.inc
@@ -1,51 +1,51 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/settings/delete_identity.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Delete the submitted identities (IIDs) from the database |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
$iid = get_input_value('_iid', RCUBE_INPUT_GPC);
// check request token
if (!$OUTPUT->ajax_call && !$RCMAIL->check_request(RCUBE_INPUT_GPC)) {
$OUTPUT->show_message('invalidrequest', 'error');
rcmail_overwrite_action('identities');
return;
}
if ($iid && preg_match('/^[0-9]+(,[0-9]+)*$/', $iid))
{
$plugin = $RCMAIL->plugins->exec_hook('identity_delete', array('id' => $iid));
- $deleted = !$plugin['abort'] ? $USER->delete_identity($iid) : $plugin['result'];
+ $deleted = !$plugin['abort'] ? $RCMAIL->user->delete_identity($iid) : $plugin['result'];
if ($deleted > 0 && $deleted !== false)
$OUTPUT->show_message('deletedsuccessfully', 'confirmation', null, false);
else
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : ($deleted < 0 ? 'nodeletelastidentity' : 'errorsaving'), 'error', null, false);
// send response
if ($OUTPUT->ajax_call)
$OUTPUT->send();
}
if ($OUTPUT->ajax_call)
exit;
// go to identities page
rcmail_overwrite_action('identities');
diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc
index a42e8d091..df5cf9328 100644
--- a/program/steps/settings/edit_identity.inc
+++ b/program/steps/settings/edit_identity.inc
@@ -1,162 +1,162 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/settings/edit_identity.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2007, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Show edit form for a identity record or to add a new one |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
define('IDENTITIES_LEVEL', intval($RCMAIL->config->get('identities_level', 0)));
// edit-identity
if (($_GET['_iid'] || $_POST['_iid']) && $RCMAIL->action=='edit-identity') {
- $IDENTITY_RECORD = $USER->get_identity(get_input_value('_iid', RCUBE_INPUT_GPC));
+ $IDENTITY_RECORD = $RCMAIL->user->get_identity(get_input_value('_iid', RCUBE_INPUT_GPC));
if (is_array($IDENTITY_RECORD))
$OUTPUT->set_env('iid', $IDENTITY_RECORD['identity_id']);
else {
$OUTPUT->show_message('dberror', 'error');
// go to identities page
rcmail_overwrite_action('identities');
return;
}
}
// add-identity
else {
if (IDENTITIES_LEVEL > 1) {
$OUTPUT->show_message('opnotpermitted', 'error');
// go to identities page
rcmail_overwrite_action('identities');
return;
}
else if (IDENTITIES_LEVEL == 1)
$IDENTITY_RECORD['email'] = $RCMAIL->user->get_username();
}
function rcube_identity_form($attrib)
{
global $IDENTITY_RECORD, $RCMAIL, $OUTPUT;
// Add HTML editor script(s)
rcube_html_editor('identity');
// add some labels to client
$OUTPUT->add_label('noemailwarning', 'nonamewarning', 'converting', 'editorwarning');
$i_size = !empty($attrib['size']) ? $attrib['size'] : 40;
$t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 6;
$t_cols = !empty($attrib['textareacols']) ? $attrib['textareacols'] : 40;
// list of available cols
$form = array(
'addressing' => array(
'name' => rcube_label('settings'),
'content' => array(
'name' => array('type' => 'text', 'size' => $i_size),
'email' => array('type' => 'text', 'size' => $i_size),
'organization' => array('type' => 'text', 'size' => $i_size),
'reply-to' => array('type' => 'text', 'size' => $i_size),
'bcc' => array('type' => 'text', 'size' => $i_size),
'standard' => array('type' => 'checkbox', 'label' => rcube_label('setdefault')),
)),
'signature' => array(
'name' => rcube_label('signature'),
'content' => array(
'signature' => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows,
'spellcheck' => true),
'html_signature' => array('type' => 'checkbox', 'label' => rcube_label('htmlsignature'),
'onclick' => 'return rcmail_toggle_editor(this, \'rcmfd_signature\');'),
))
);
// Enable TinyMCE editor
if ($IDENTITY_RECORD['html_signature']) {
$form['signature']['content']['signature']['class'] = 'mce_editor';
}
// disable some field according to access level
if (IDENTITIES_LEVEL == 1 || IDENTITIES_LEVEL == 3) {
$form['addressing']['content']['email']['disabled'] = true;
$form['addressing']['content']['email']['class'] = 'disabled';
}
$IDENTITY_RECORD['email'] = rcube_idn_to_utf8($IDENTITY_RECORD['email']);
$IDENTITY_RECORD['reply-to'] = rcube_idn_to_utf8($IDENTITY_RECORD['reply-to']);
$IDENTITY_RECORD['bcc'] = rcube_idn_to_utf8($IDENTITY_RECORD['bcc']);
// Allow plugins to modify identity form content
$plugin = $RCMAIL->plugins->exec_hook('identity_form', array(
'form' => $form, 'record' => $IDENTITY_RECORD));
$form = $plugin['form'];
$IDENTITY_RECORD = $plugin['record'];
// Set form tags and hidden fields
list($form_start, $form_end) = get_form_tags($attrib, 'save-identity',
intval($IDENTITY_RECORD['identity_id']),
array('name' => '_iid', 'value' => $IDENTITY_RECORD['identity_id']));
unset($plugin);
unset($attrib['form']);
// return the complete edit form as table
$out = "$form_start\n";
foreach ($form as $fieldset) {
if (empty($fieldset['content']))
continue;
$content = '';
if (is_array($fieldset['content'])) {
$table = new html_table(array('cols' => 2));
foreach ($fieldset['content'] as $col => $colprop) {
$colprop['id'] = 'rcmfd_'.$col;
$label = !empty($colprop['label']) ? $colprop['label'] :
rcube_label(str_replace('-', '', $col));
$value = !empty($colprop['value']) ? $colprop['value'] :
rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $colprop, $colprop['type']);
$table->add('title', sprintf('<label for="%s">%s</label>', $colprop['id'], Q($label)));
$table->add(null, $value);
}
$content = $table->show();
}
else {
$content = $fieldset['content'];
}
$out .= html::tag('fieldset', null, html::tag('legend', null, Q($fieldset['name'])) . $content) ."\n";
}
$out .= $form_end;
return $out;
}
$OUTPUT->include_script('list.js');
$OUTPUT->add_handler('identityform', 'rcube_identity_form');
$OUTPUT->set_env('identities_level', IDENTITIES_LEVEL);
$OUTPUT->set_pagetitle(rcube_label(($RCMAIL->action=='add-identity' ? 'newidentity' : 'edititem')));
if ($RCMAIL->action=='add-identity' && $OUTPUT->template_exists('identityadd'))
$OUTPUT->send('identityadd');
$OUTPUT->send('identityedit');
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 65836a958..ac55acb3a 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -1,915 +1,915 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/settings/func.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2007, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Provide functionality for user's settings & preferences |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
if (!$OUTPUT->ajax_call)
$OUTPUT->set_pagetitle(rcube_label('preferences'));
// similar function as /steps/settings/identities.inc::rcmail_identity_frame()
function rcmail_preferences_frame($attrib)
{
global $OUTPUT;
if (!$attrib['id'])
$attrib['id'] = 'rcmprefsframe';
$attrib['name'] = $attrib['id'];
$OUTPUT->set_env('contentframe', $attrib['name']);
$OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
return html::iframe($attrib);
}
function rcmail_sections_list($attrib)
{
global $RCMAIL;
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmsectionslist';
list($list, $cols) = rcmail_user_prefs();
// create XHTML table
$out = rcube_table_output($attrib, $list, $cols, 'id');
// set client env
$RCMAIL->output->add_gui_object('sectionslist', $attrib['id']);
$RCMAIL->output->include_script('list.js');
return $out;
}
function rcmail_identities_list($attrib)
{
- global $OUTPUT, $USER, $RCMAIL;
+ global $OUTPUT, $RCMAIL;
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmIdentitiesList';
// get identities list and define 'mail' column
- $list = $USER->list_identities();
+ $list = $RCMAIL->user->list_identities();
foreach ($list as $idx => $row)
$list[$idx]['mail'] = trim($row['name'] . ' <' . rcube_idn_to_utf8($row['email']) .'>');
// get all identites from DB and define list of cols to be displayed
$plugin = $RCMAIL->plugins->exec_hook('identities_list', array(
'list' => $list,
'cols' => array('mail')));
// @TODO: use <UL> instead of <TABLE> for identities list
// create XHTML table
$out = rcube_table_output($attrib, $plugin['list'], $plugin['cols'], 'identity_id');
// set client env
$OUTPUT->add_gui_object('identitieslist', $attrib['id']);
return $out;
}
// similar function as in /steps/addressbook/edit.inc
function get_form_tags($attrib, $action, $id = null, $hidden = null)
{
global $EDIT_FORM, $RCMAIL;
$form_start = $form_end = '';
if (empty($EDIT_FORM)) {
$request_key = $action . (isset($id) ? '.'.$id : '');
$form_start = $RCMAIL->output->request_form(array(
'name' => 'form',
'method' => 'post',
'task' => $RCMAIL->task,
'action' => $action,
'request' => $request_key,
'noclose' => true
) + $attrib);
if (is_array($hidden)) {
$hiddenfields = new html_hiddenfield($hidden);
$form_start .= $hiddenfields->show();
}
$form_end = !strlen($attrib['form']) ? '</form>' : '';
$EDIT_FORM = !empty($attrib['form']) ? $attrib['form'] : 'form';
$RCMAIL->output->add_gui_object('editform', $EDIT_FORM);
}
return array($form_start, $form_end);
}
function rcmail_user_prefs($current=null)
{
global $RCMAIL;
$sections['general'] = array('id' => 'general', 'section' => rcube_label('uisettings'));
$sections['mailbox'] = array('id' => 'mailbox', 'section' => rcube_label('mailboxview'));
$sections['compose'] = array('id' => 'compose', 'section' => rcube_label('messagescomposition'));
$sections['mailview'] = array('id' => 'mailview','section' => rcube_label('messagesdisplaying'));
$sections['addressbook'] = array('id' => 'addressbook','section' => rcube_label('addressbook'));
$sections['folders'] = array('id' => 'folders', 'section' => rcube_label('specialfolders'));
$sections['server'] = array('id' => 'server', 'section' => rcube_label('serversettings'));
// hook + define list cols
$plugin = $RCMAIL->plugins->exec_hook('preferences_sections_list',
array('list' => $sections, 'cols' => array('section')));
$sections = $plugin['list'];
$config = $RCMAIL->config->all();
$no_override = array_flip($RCMAIL->config->get('dont_override', array()));
foreach ($sections as $idx => $sect) {
if ($current && $sect['id'] != $current)
continue;
$blocks = array();
switch ($sect['id']) {
// general
case 'general':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
'list' => array('name' => Q(rcube_label('listoptions'))),
);
// language selection
if (!isset($no_override['language'])) {
$a_lang = $RCMAIL->list_languages();
asort($a_lang);
$field_id = 'rcmfd_lang';
$select_lang = new html_select(array('name' => '_language', 'id' => $field_id));
$select_lang->add(array_values($a_lang), array_keys($a_lang));
$blocks['main']['options']['language'] = array(
'title' => html::label($field_id, Q(rcube_label('language'))),
'content' => $select_lang->show($RCMAIL->user->language),
);
}
// show page size selection
if (!isset($no_override['timezone'])) {
$field_id = 'rcmfd_timezone';
$select_timezone = new html_select(array('name' => '_timezone', 'id' => $field_id, 'onchange' => "$('#rcmfd_dst').attr('disabled', this.selectedIndex==0)"));
$select_timezone->add(rcube_label('autodetect'), 'auto');
$select_timezone->add('(GMT -11:00) Midway Island, Samoa', '-11');
$select_timezone->add('(GMT -10:00) Hawaii', '-10');
$select_timezone->add('(GMT -9:30) Marquesas Islands', '-9.5');
$select_timezone->add('(GMT -9:00) Alaska', '-9');
$select_timezone->add('(GMT -8:00) Pacific Time (US/Canada)', '-8');
$select_timezone->add('(GMT -7:00) Mountain Time (US/Canada)', '-7');
$select_timezone->add('(GMT -6:00) Central Time (US/Canada), Mexico City', '-6');
$select_timezone->add('(GMT -5:00) Eastern Time (US/Canada), Bogota, Lima', '-5');
$select_timezone->add('(GMT -4:30) Caracas', '-4.5');
$select_timezone->add('(GMT -4:00) Atlantic Time (Canada), La Paz', '-4');
$select_timezone->add('(GMT -3:30) Nfld Time (Canada), Nfld, S. Labador', '-3.5');
$select_timezone->add('(GMT -3:00) Brazil, Buenos Aires, Georgetown', '-3');
$select_timezone->add('(GMT -2:00) Mid-Atlantic', '-2');
$select_timezone->add('(GMT -1:00) Azores, Cape Verde Islands', '-1');
$select_timezone->add('(GMT) Western Europe, London, Lisbon, Casablanca', '0');
$select_timezone->add('(GMT +1:00) Central European Time', '1');
$select_timezone->add('(GMT +2:00) EET: Tallinn, Helsinki, Kaliningrad, South Africa', '2');
$select_timezone->add('(GMT +3:00) Baghdad, Kuwait, Riyadh, Moscow, Nairobi', '3');
$select_timezone->add('(GMT +3:30) Tehran', '3.5');
$select_timezone->add('(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi', '4');
$select_timezone->add('(GMT +4:30) Kabul', '4.5');
$select_timezone->add('(GMT +5:00) Ekaterinburg, Islamabad, Karachi', '5');
$select_timezone->add('(GMT +5:30) Chennai, Kolkata, Mumbai, New Delhi', '5.5');
$select_timezone->add('(GMT +5:45) Kathmandu', '5.75');
$select_timezone->add('(GMT +6:00) Almaty, Dhaka, Colombo', '6');
$select_timezone->add('(GMT +6:30) Cocos Islands, Myanmar', '6.5');
$select_timezone->add('(GMT +7:00) Bangkok, Hanoi, Jakarta', '7');
$select_timezone->add('(GMT +8:00) Beijing, Perth, Singapore, Taipei', '8');
$select_timezone->add('(GMT +8:45) Caiguna, Eucla, Border Village', '8.75');
$select_timezone->add('(GMT +9:00) Tokyo, Seoul, Yakutsk', '9');
$select_timezone->add('(GMT +9:30) Adelaide, Darwin', '9.5');
$select_timezone->add('(GMT +10:00) EAST/AEST: Sydney, Guam, Vladivostok', '10');
$select_timezone->add('(GMT +10:30) New South Wales', '10.5');
$select_timezone->add('(GMT +11:00) Magadan, Solomon Islands', '11');
$select_timezone->add('(GMT +11:30) Norfolk Island', '11.5');
$select_timezone->add('(GMT +12:00) Auckland, Wellington, Kamchatka', '12');
$select_timezone->add('(GMT +12:45) Chatham Islands', '12.75');
$select_timezone->add('(GMT +13:00) Tonga, Pheonix Islands', '13');
$select_timezone->add('(GMT +14:00) Kiribati', '14');
$blocks['main']['options']['timezone'] = array(
'title' => html::label($field_id, Q(rcube_label('timezone'))),
'content' => $select_timezone->show((string)$config['timezone']),
);
}
// daylight savings
if (!isset($no_override['dst_active'])) {
$field_id = 'rcmfd_dst';
$input_dst = new html_checkbox(array('name' => '_dst_active', 'id' => $field_id, 'value' => 1, 'disabled' => ($config['timezone'] === 'auto')));
$blocks['main']['options']['dstactive'] = array(
'title' => html::label($field_id, Q(rcube_label('dstactive'))),
'content' => $input_dst->show($config['dst_active']),
);
}
// date/time formatting
if (!isset($no_override['time_format'])) {
$reftime = mktime(7,30,0);
$field_id = 'rcmfd_time_format';
$select_time = new html_select(array('name' => '_time_format', 'id' => $field_id));
foreach ((array)$RCMAIL->config->get('time_formats', array('G:i', 'H:i', 'g:i a', 'h:i A')) as $choice)
$select_time->add(date($choice, $reftime), $choice);
$blocks['main']['options']['time_format'] = array(
'title' => html::label($field_id, Q(rcube_label('timeformat'))),
'content' => $select_time->show($RCMAIL->config->get('time_format')),
);
}
if (!isset($no_override['date_format'])) {
$refdate = mktime(12,30,0,7,24);
$field_id = 'rcmfd_date_format';
$select_date = new html_select(array('name' => '_date_format', 'id' => $field_id));
foreach ((array)$RCMAIL->config->get('date_formats', array('Y-m-d','d-m-Y','Y/m/d','m/d/Y','d/m/Y','d.m.Y','j.n.Y')) as $choice)
$select_date->add(date($choice, $refdate), $choice);
$blocks['main']['options']['date_format'] = array(
'title' => html::label($field_id, Q(rcube_label('dateformat'))),
'content' => $select_date->show($config['date_format']),
);
}
// MM: Show checkbox for toggling 'pretty dates'
if (!isset($no_override['prettydate'])) {
$field_id = 'rcmfd_prettydate';
$input_prettydate = new html_checkbox(array('name' => '_pretty_date', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['prettydate'] = array(
'title' => html::label($field_id, Q(rcube_label('prettydate'))),
'content' => $input_prettydate->show($config['prettydate']?1:0),
);
}
// show page size selection
if (!isset($no_override['pagesize'])) {
$field_id = 'rcmfd_pgsize';
$input_pagesize = new html_inputfield(array('name' => '_pagesize', 'id' => $field_id, 'size' => 5));
$blocks['list']['options']['pagesize'] = array(
'title' => html::label($field_id, Q(rcube_label('pagesize'))),
'content' => $input_pagesize->show($config['pagesize']),
);
}
// show drop-down for available skins
if (!isset($no_override['skin'])) {
$skins = rcmail_get_skins();
if (count($skins) > 1) {
$field_id = 'rcmfd_skin';
$input_skin = new html_select(array('name'=>'_skin', 'id'=>$field_id));
foreach($skins as $skin)
$input_skin->add($skin, $skin);
$blocks['main']['options']['skin'] = array(
'title' => html::label($field_id, Q(rcube_label('skin'))),
'content' => $input_skin->show($config['skin']),
);
}
}
break;
// Mailbox view (mail screen)
case 'mailbox':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
'new_message' => array('name' => Q(rcube_label('newmessage'))),
);
// show config parameter for preview pane
if (!isset($no_override['preview_pane'])) {
$field_id = 'rcmfd_preview';
$input_preview = new html_checkbox(array('name' => '_preview_pane', 'id' => $field_id, 'value' => 1,
'onchange' => JS_OBJECT_NAME.'.toggle_preview_pane(this)'));
$blocks['main']['options']['preview_pane'] = array(
'title' => html::label($field_id, Q(rcube_label('previewpane'))),
'content' => $input_preview->show($config['preview_pane']?1:0),
);
}
// show config parameter for preview pane auto mark as read delay
if (!isset($no_override['preview_pane_mark_read'])) {
// apply default if config option is not set at all
$config['preview_pane_mark_read'] = $RCMAIL->config->get('preview_pane_mark_read', 0);
$field_id = 'rcmfd_preview_pane_mark_read';
$select_delay = new html_select(array('name' => '_preview_pane_mark_read', 'id' => $field_id,
'disabled' => $config['preview_pane']?0:1));
$select_delay->add(rcube_label('never'), '-1');
$select_delay->add(rcube_label('immediately'), 0);
foreach(array(5, 10, 20, 30) as $sec)
$select_delay->add(rcube_label(array('name' => 'afternseconds', 'vars' => array('n' => $sec))), $sec);
$blocks['main']['options']['preview_pane_mark_read'] = array(
'title' => html::label($field_id, Q(rcube_label('previewpanemarkread'))),
'content' => $select_delay->show(intval($config['preview_pane_mark_read'])),
);
}
if (!isset($no_override['mdn_requests'])) {
$field_id = 'rcmfd_mdn_requests';
$select_mdn_requests = new html_select(array('name' => '_mdn_requests', 'id' => $field_id));
$select_mdn_requests->add(rcube_label('askuser'), 0);
$select_mdn_requests->add(rcube_label('autosend'), 1);
$select_mdn_requests->add(rcube_label('autosendknown'), 3);
$select_mdn_requests->add(rcube_label('autosendknownignore'), 4);
$select_mdn_requests->add(rcube_label('ignore'), 2);
$blocks['main']['options']['mdn_requests'] = array(
'title' => html::label($field_id, Q(rcube_label('mdnrequests'))),
'content' => $select_mdn_requests->show($config['mdn_requests']),
);
}
$RCMAIL->imap_connect();
$threading_supported = $RCMAIL->imap->get_capability('THREAD');
if (!isset($no_override['autoexpand_threads']) && $threading_supported) {
$field_id = 'rcmfd_autoexpand_threads';
$select_autoexpand_threads = new html_select(array('name' => '_autoexpand_threads', 'id' => $field_id));
$select_autoexpand_threads->add(rcube_label('never'), 0);
$select_autoexpand_threads->add(rcube_label('do_expand'), 1);
$select_autoexpand_threads->add(rcube_label('expand_only_unread'), 2);
$blocks['main']['options']['autoexpand_threads'] = array(
'title' => html::label($field_id, Q(rcube_label('autoexpand_threads'))),
'content' => $select_autoexpand_threads->show($config['autoexpand_threads']),
);
}
if (!isset($no_override['keep_alive'])) {
$field_id = 'rcmfd_keep_alive';
$select_keep_alive = new html_select(array('name' => '_keep_alive', 'id' => $field_id));
foreach(array(1, 3, 5, 10, 15, 30, 60) as $min)
if((!$config['min_keep_alive'] || $config['min_keep_alive'] <= $min * 60)
&& (!$config['session_lifetime'] || $config['session_lifetime'] > $min)) {
$select_keep_alive->add(rcube_label(array('name' => 'everynminutes', 'vars' => array('n' => $min))), $min);
}
$blocks['new_message']['options']['keep_alive'] = array(
'title' => html::label($field_id, Q(rcube_label('keepalive'))),
'content' => $select_keep_alive->show($config['keep_alive']/60),
);
}
if (!isset($no_override['check_all_folders'])) {
$field_id = 'rcmfd_check_all_folders';
$input_check_all = new html_checkbox(array('name' => '_check_all_folders', 'id' => $field_id, 'value' => 1));
$blocks['new_message']['options']['check_all_folders'] = array(
'title' => html::label($field_id, Q(rcube_label('checkallfolders'))),
'content' => $input_check_all->show($config['check_all_folders']?1:0),
);
}
break;
// Message viewing
case 'mailview':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
);
// show checkbox for HTML/plaintext messages
if (!isset($no_override['prefer_html'])) {
$field_id = 'rcmfd_htmlmsg';
$input_preferhtml = new html_checkbox(array('name' => '_prefer_html', 'id' => $field_id, 'value' => 1,
'onchange' => JS_OBJECT_NAME.'.toggle_prefer_html(this)'));
$blocks['main']['options']['prefer_html'] = array(
'title' => html::label($field_id, Q(rcube_label('preferhtml'))),
'content' => $input_preferhtml->show($config['prefer_html']?1:0),
);
}
if (!isset($no_override['default_charset'])) {
$field_id = 'rcmfd_default_charset';
$blocks['main']['options']['default_charset'] = array(
'title' => html::label($field_id, Q(rcube_label('defaultcharset'))),
'content' => $RCMAIL->output->charset_selector(array(
'name' => '_default_charset', 'selected' => $config['default_charset']
))
);
}
if (!isset($no_override['show_images'])) {
$field_id = 'rcmfd_show_images';
$input_show_images = new html_select(array('name' => '_show_images', 'id' => $field_id,
'disabled' => !$config['prefer_html']));
$input_show_images->add(rcube_label('never'), 0);
$input_show_images->add(rcube_label('fromknownsenders'), 1);
$input_show_images->add(rcube_label('always'), 2);
$blocks['main']['options']['show_images'] = array(
'title' => html::label($field_id, Q(rcube_label('showremoteimages'))),
'content' => $input_show_images->show($config['prefer_html'] ? $config['show_images'] : 0),
);
}
if (!isset($no_override['inline_images'])) {
$field_id = 'rcmfd_inline_images';
$input_inline_images = new html_checkbox(array('name' => '_inline_images', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['inline_images'] = array(
'title' => html::label($field_id, Q(rcube_label('showinlineimages'))),
'content' => $input_inline_images->show($config['inline_images']?1:0),
);
}
// "display after delete" checkbox
if (!isset($no_override['display_next'])) {
$field_id = 'rcmfd_displaynext';
$input_displaynext = new html_checkbox(array('name' => '_display_next', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['display_next'] = array(
'title' => html::label($field_id, Q(rcube_label('displaynext'))),
'content' => $input_displaynext->show($config['display_next']?1:0),
);
}
break;
// Mail composition
case 'compose':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))),
'sig' => array('name' => Q(rcube_label('signatureoptions'))),
);
if (!isset($no_override['htmleditor'])) {
$field_id = 'rcmfd_htmleditor';
$select_htmleditor = new html_select(array('name' => '_htmleditor', 'id' => $field_id));
$select_htmleditor->add(rcube_label('never'), 0);
$select_htmleditor->add(rcube_label('always'), 1);
$select_htmleditor->add(rcube_label('htmlonreply'), 2);
$blocks['main']['options']['htmleditor'] = array(
'title' => html::label($field_id, Q(rcube_label('htmleditor'))),
'content' => $select_htmleditor->show(intval($config['htmleditor'])),
);
}
if (!isset($no_override['draft_autosave'])) {
$field_id = 'rcmfd_autosave';
$select_autosave = new html_select(array('name' => '_draft_autosave', 'id' => $field_id, 'disabled' => empty($config['drafts_mbox'])));
$select_autosave->add(rcube_label('never'), 0);
foreach (array(1, 3, 5, 10) as $i => $min)
$select_autosave->add(rcube_label(array('name' => 'everynminutes', 'vars' => array('n' => $min))), $min*60);
$blocks['main']['options']['draft_autosave'] = array(
'title' => html::label($field_id, Q(rcube_label('autosavedraft'))),
'content' => $select_autosave->show($config['draft_autosave']),
);
}
if (!isset($no_override['mime_param_folding'])) {
$field_id = 'rcmfd_param_folding';
$select_param_folding = new html_select(array('name' => '_mime_param_folding', 'id' => $field_id));
$select_param_folding->add(rcube_label('2231folding'), 0);
$select_param_folding->add(rcube_label('miscfolding'), 1);
$select_param_folding->add(rcube_label('2047folding'), 2);
$blocks['main']['options']['mime_param_folding'] = array(
'advanced' => true,
'title' => html::label($field_id, Q(rcube_label('mimeparamfolding'))),
'content' => $select_param_folding->show($config['mime_param_folding']),
);
}
if (!isset($no_override['force_7bit'])) {
$field_id = 'rcmfd_force_7bit';
$input_7bit = new html_checkbox(array('name' => '_force_7bit', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['force_7bit'] = array(
'title' => html::label($field_id, Q(rcube_label('force7bit'))),
'content' => $input_7bit->show($config['force_7bit']?1:0),
);
}
if (!isset($no_override['mdn_default'])) {
$field_id = 'rcmfd_mdn_default';
$input_mdn = new html_checkbox(array('name' => '_mdn_default', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['mdn_default'] = array(
'title' => html::label($field_id, Q(rcube_label('reqmdn'))),
'content' => $input_mdn->show($config['mdn_default']?1:0),
);
}
if (!isset($no_override['dsn_default'])) {
$field_id = 'rcmfd_dsn_default';
$input_dsn = new html_checkbox(array('name' => '_dsn_default', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['dsn_default'] = array(
'title' => html::label($field_id, Q(rcube_label('reqdsn'))),
'content' => $input_dsn->show($config['dsn_default']?1:0),
);
}
if (!isset($no_override['reply_same_folder'])) {
$field_id = 'rcmfd_reply_same_folder';
$input_reply_same_folder = new html_checkbox(array('name' => '_reply_same_folder', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['reply_same_folder'] = array(
'title' => html::label($field_id, Q(rcube_label('replysamefolder'))),
'content' => $input_reply_same_folder->show($config['reply_same_folder']?1:0),
);
}
if (!isset($no_override['top_posting'])) {
$field_id = 'rcmfd_top_posting';
$select_replymode = new html_select(array('name' => '_top_posting', 'id' => $field_id, 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex==0)"));
$select_replymode->add(rcube_label('replybottomposting'), 0);
$select_replymode->add(rcube_label('replytopposting'), 1);
$blocks['main']['options']['top_posting'] = array(
'title' => html::label($field_id, Q(rcube_label('whenreplying'))),
'content' => $select_replymode->show($config['top_posting']?1:0),
);
}
if (!isset($no_override['spellcheck_before_send']) && $config['enable_spellcheck']) {
$field_id = 'rcmfd_spellcheck_before_send';
$input_spellcheck = new html_checkbox(array('name' => '_spellcheck_before_send', 'id' => $field_id, 'value' => 1));
$blocks['spellcheck']['options']['spellcheck_before_send'] = array(
'title' => html::label($field_id, Q(rcube_label('spellcheckbeforesend'))),
'content' => $input_spellcheck->show($config['spellcheck_before_send']?1:0),
);
}
if ($config['enable_spellcheck']) {
foreach (array('syms', 'nums', 'caps') as $key) {
$key = 'spellcheck_ignore_'.$key;
if (!isset($no_override[$key])) {
$input_spellcheck = new html_checkbox(array('name' => '_'.$key, 'id' => 'rcmfd_'.$key, 'value' => 1));
$blocks['spellcheck']['options'][$key] = array(
'title' => html::label($field_id, Q(rcube_label(str_replace('_', '', $key)))),
'content' => $input_spellcheck->show($config[$key]?1:0),
);
}
}
}
if (!isset($no_override['show_sig'])) {
$field_id = 'rcmfd_show_sig';
$select_show_sig = new html_select(array('name' => '_show_sig', 'id' => $field_id));
$select_show_sig->add(rcube_label('never'), 0);
$select_show_sig->add(rcube_label('always'), 1);
$select_show_sig->add(rcube_label('newmessageonly'), 2);
$select_show_sig->add(rcube_label('replyandforwardonly'), 3);
$blocks['sig']['options']['show_sig'] = array(
'title' => html::label($field_id, Q(rcube_label('autoaddsignature'))),
'content' => $select_show_sig->show($RCMAIL->config->get('show_sig', 1)),
);
}
if (!isset($no_override['sig_above'])) {
$field_id = 'rcmfd_sig_above';
$select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => !$config['top_posting']));
$select_sigabove->add(rcube_label('belowquote'), 0);
$select_sigabove->add(rcube_label('abovequote'), 1);
$blocks['sig']['options']['sig_above'] = array(
'title' => html::label($field_id, Q(rcube_label('replysignaturepos'))),
'content' => $select_sigabove->show($config['sig_above']?1:0),
);
}
if (!isset($no_override['strip_existing_sig'])) {
$field_id = 'rcmfd_strip_existing_sig';
$input_stripexistingsig = new html_checkbox(array('name' => '_strip_existing_sig', 'id' => $field_id, 'value' => 1));
$blocks['sig']['options']['strip_existing_sig'] = array(
'title' => html::label($field_id, Q(rcube_label('replyremovesignature'))),
'content' => $input_stripexistingsig->show($config['strip_existing_sig']?1:0),
);
}
if (!isset($no_override['default_font'])) {
$field_id = 'rcmfd_default_font';
$fonts = rcube_fontdefs();
$default_font = $config['default_font'] ? $config['default_font'] : 'Verdana';
$select = '<select name="_default_font" id="'.$field_id.'">';
foreach ($fonts as $fname => $font)
$select .= '<option value="'.$fname.'"'
. ($fname == $default_font ? ' selected="selected"' : '')
. ' style=\'font-family: ' . $font . '\'>'
. Q($fname) . '</option>';
$select .= '</select>';
$blocks['main']['options']['default_font'] = array(
'title' => html::label($field_id, Q(rcube_label('defaultfont'))),
'content' => $select
);
}
break;
// Addressbook config
case 'addressbook':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
);
if (!isset($no_override['default_addressbook'])
&& ($books = $RCMAIL->get_address_sources(true))
) {
$field_id = 'rcmfd_default_addressbook';
$select_abook = new html_select(array('name' => '_default_addressbook', 'id' => $field_id));
foreach ($books as $book) {
$select_abook->add($book['name'], $book['id']);
}
$blocks['main']['options']['default_addressbook'] = array(
'title' => html::label($field_id, Q(rcube_label('defaultaddressbook'))),
'content' => $select_abook->show($config['default_addressbook']),
);
}
if (!isset($no_override['autocomplete_single'])) {
$field_id = 'rcmfd_autocomplete_single';
$checkbox = new html_checkbox(array('name' => '_autocomplete_single', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['autocomplete_single'] = array(
'title' => html::label($field_id, Q(rcube_label('autocompletesingle'))),
'content' => $checkbox->show($config['autocomplete_single']?1:0),
);
}
break;
// Special IMAP folders
case 'folders':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
);
// Configure special folders
if (!isset($no_override['default_imap_folders'])) {
$RCMAIL->imap_connect();
// load folders list only when needed
if ($current) {
$select = rcmail_mailbox_select(array('noselection' => '---', 'realnames' => true,
'maxlength' => 30, 'exceptions' => array('INBOX'), 'folder_filter' => 'mail', 'folder_rights' => 'w'));
}
else // dummy select
$select = new html_select();
if (!isset($no_override['drafts_mbox']))
$blocks['main']['options']['drafts_mbox'] = array(
'title' => Q(rcube_label('drafts')),
'content' => $select->show($config['drafts_mbox'], array('name' => "_drafts_mbox")),
);
if (!isset($no_override['sent_mbox']))
$blocks['main']['options']['sent_mbox'] = array(
'title' => Q(rcube_label('sent')),
'content' => $select->show($config['sent_mbox'], array('name' => "_sent_mbox")),
);
if (!isset($no_override['junk_mbox']))
$blocks['main']['options']['junk_mbox'] = array(
'title' => Q(rcube_label('junk')),
'content' => $select->show($config['junk_mbox'], array('name' => "_junk_mbox")),
);
if (!isset($no_override['trash_mbox']))
$blocks['main']['options']['trash_mbox'] = array(
'title' => Q(rcube_label('trash')),
'content' => $select->show($config['trash_mbox'], array('name' => "_trash_mbox")),
);
}
break;
// Server settings
case 'server':
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
'maintenance' => array('name' => Q(rcube_label('maintenance'))),
);
if (!isset($no_override['read_when_deleted'])) {
$field_id = 'rcmfd_read_deleted';
$input_readdeleted = new html_checkbox(array('name' => '_read_when_deleted', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['read_when_deleted'] = array(
'title' => html::label($field_id, Q(rcube_label('readwhendeleted'))),
'content' => $input_readdeleted->show($config['read_when_deleted']?1:0),
);
}
if (!isset($no_override['flag_for_deletion'])) {
$field_id = 'rcmfd_flag_for_deletion';
$input_flagfordeletion = new html_checkbox(array('name' => '_flag_for_deletion', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['flag_for_deletion'] = array(
'title' => html::label($field_id, Q(rcube_label('flagfordeletion'))),
'content' => $input_flagfordeletion->show($config['flag_for_deletion']?1:0),
);
}
// don't show deleted messages
if (!isset($no_override['skip_deleted'])) {
$field_id = 'rcmfd_skip_deleted';
$input_purge = new html_checkbox(array('name' => '_skip_deleted', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['skip_deleted'] = array(
'title' => html::label($field_id, Q(rcube_label('skipdeleted'))),
'content' => $input_purge->show($config['skip_deleted']?1:0),
);
}
if (!isset($no_override['delete_always'])) {
$field_id = 'rcmfd_delete_always';
$input_delete_always = new html_checkbox(array('name' => '_delete_always', 'id' => $field_id, 'value' => 1));
$blocks['main']['options']['delete_always'] = array(
'title' => html::label($field_id, Q(rcube_label('deletealways'))),
'content' => $input_delete_always->show($config['delete_always']?1:0),
);
}
// Trash purging on logout
if (!isset($no_override['logout_purge'])) {
$field_id = 'rcmfd_logout_purge';
$input_purge = new html_checkbox(array('name' => '_logout_purge', 'id' => $field_id, 'value' => 1));
$blocks['maintenance']['options']['logout_purge'] = array(
'title' => html::label($field_id, Q(rcube_label('logoutclear'))),
'content' => $input_purge->show($config['logout_purge']?1:0),
);
}
// INBOX compacting on logout
if (!isset($no_override['logout_expunge'])) {
$field_id = 'rcmfd_logout_expunge';
$input_expunge = new html_checkbox(array('name' => '_logout_expunge', 'id' => $field_id, 'value' => 1));
$blocks['maintenance']['options']['logout_expunge'] = array(
'title' => html::label($field_id, Q(rcube_label('logoutcompact'))),
'content' => $input_expunge->show($config['logout_expunge']?1:0),
);
}
break;
}
$data = $RCMAIL->plugins->exec_hook('preferences_list', array('section' => $sect['id'], 'blocks' => $blocks));
$found = false;
// create output
foreach ($data['blocks'] as $block) {
if (!empty($block['content']) || !empty($block['options'])) {
$found = true;
break;
}
}
if (!$found)
unset($sections[$idx]);
else
$sections[$idx]['blocks'] = $data['blocks'];
}
return array($sections, $plugin['cols']);
}
function rcmail_get_skins()
{
$path = 'skins';
$skins = array();
$dir = opendir($path);
if (!$dir)
return false;
while (($file = readdir($dir)) !== false)
{
$filename = $path.'/'.$file;
if (!preg_match('/^\./', $file) && is_dir($filename) && is_readable($filename))
$skins[] = $file;
}
closedir($dir);
return $skins;
}
function rcmail_folder_options($mailbox)
{
global $RCMAIL;
$options = $RCMAIL->imap->mailbox_info($mailbox);
$options['protected'] = $options['is_root'] || ($options['special'] && $RCMAIL->config->get('protect_default_folders'));
return $options;
}
/**
* Updates (or creates) folder row in the subscriptions table
*
* @param string $name Folder name
* @param string $oldname Old folder name (for update)
* @param bool $subscribe Checks subscription checkbox
* @param string $class CSS class name for folder row
*/
function rcmail_update_folder_row($name, $oldname=null, $subscribe=false, $class_name=null)
{
global $RCMAIL, $CONFIG, $OUTPUT;
$delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
$name_utf8 = rcube_charset_convert($name, 'UTF7-IMAP');
$protected = ($CONFIG['protect_default_folders'] == true && in_array($name, $CONFIG['default_imap_folders']));
$foldersplit = explode($delimiter, $RCMAIL->imap->mod_mailbox($name));
$level = count($foldersplit) - 1;
$display_name = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level)
. Q($protected ? rcmail_localize_foldername($name) : rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP'));
if ($oldname === null)
$OUTPUT->command('add_folder_row', $name_utf8, $display_name, $protected, $subscribe,
false, $class_name);
else
$OUTPUT->command('replace_folder_row', rcube_charset_convert($oldname, 'UTF7-IMAP'),
$name_utf8, $display_name, $protected, $class_name);
}
// register UI objects
$OUTPUT->add_handlers(array(
'prefsframe' => 'rcmail_preferences_frame',
'sectionslist' => 'rcmail_sections_list',
'identitieslist' => 'rcmail_identities_list',
));
// register action aliases
$RCMAIL->register_action_map(array(
'folders' => 'folders.inc',
'rename-folder' => 'folders.inc',
'delete-folder' => 'folders.inc',
'subscribe' => 'folders.inc',
'unsubscribe' => 'folders.inc',
'purge' => 'folders.inc',
'folder-size' => 'folders.inc',
'add-identity' => 'edit_identity.inc',
));
diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc
index f493f7385..f88e732d9 100644
--- a/program/steps/settings/save_identity.inc
+++ b/program/steps/settings/save_identity.inc
@@ -1,155 +1,155 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/settings/save_identity.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Save an identity record or to add a new one |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
define('IDENTITIES_LEVEL', intval($RCMAIL->config->get('identities_level', 0)));
$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'standard', 'signature', 'html_signature');
$a_html_cols = array('signature', 'name', 'organization');
$a_boolean_cols = array('standard', 'html_signature');
$updated = $default_id = false;
// check input
if (empty($_POST['_name']) || (empty($_POST['_email']) && IDENTITIES_LEVEL != 1 && IDENTITIES_LEVEL != 3))
{
$OUTPUT->show_message('formincomplete', 'warning');
rcmail_overwrite_action('edit-identity');
return;
}
$save_data = array();
foreach ($a_save_cols as $col)
{
$fname = '_'.$col;
if (isset($_POST[$fname]))
$save_data[$col] = get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols));
}
// set "off" values for checkboxes that were not checked, and therefore
// not included in the POST body.
foreach ($a_boolean_cols as $col)
{
$fname = '_' . $col;
if (!isset($_POST[$fname]))
$save_data[$col] = 0;
}
// unset email address if user has no rights to change it
if (IDENTITIES_LEVEL == 1 || IDENTITIES_LEVEL == 3)
unset($save_data['email']);
// Validate e-mail addresses
foreach (array('email', 'reply-to', 'bcc') as $item) {
if ($email = $save_data[$item]) {
$ascii_email = rcube_idn_to_ascii($email);
if (!check_email($ascii_email)) {
// show error message
$OUTPUT->show_message('emailformaterror', 'error', array('email' => $email), false);
rcmail_overwrite_action('edit-identity');
return;
}
}
}
// update an existing contact
if ($_POST['_iid'])
{
$iid = get_input_value('_iid', RCUBE_INPUT_POST);
$plugin = $RCMAIL->plugins->exec_hook('identity_update', array('id' => $iid, 'record' => $save_data));
$save_data = $plugin['record'];
if ($save_data['email'])
$save_data['email'] = rcube_idn_to_ascii($save_data['email']);
if ($save_data['bcc'])
$save_data['bcc'] = rcube_idn_to_ascii($save_data['bcc']);
if ($save_data['reply-to'])
$save_data['reply-to'] = rcube_idn_to_ascii($save_data['reply-to']);
if (!$plugin['abort'])
- $updated = $USER->update_identity($iid, $save_data);
+ $updated = $RCMAIL->user->update_identity($iid, $save_data);
else
$updated = $plugin['result'];
if ($updated) {
$OUTPUT->show_message('successfullysaved', 'confirmation');
if (!empty($_POST['_standard']))
$default_id = get_input_value('_iid', RCUBE_INPUT_POST);
if ($_POST['_framed']) {
// update the changed col in list
// ...
}
}
else {
// show error message
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error', null, false);
rcmail_overwrite_action('edit-identity');
return;
}
}
// insert a new identity record
else if (IDENTITIES_LEVEL < 2)
{
if (IDENTITIES_LEVEL == 1)
$save_data['email'] = $RCMAIL->user->get_username();
$plugin = $RCMAIL->plugins->exec_hook('identity_create', array('record' => $save_data));
$save_data = $plugin['record'];
if ($save_data['email'])
$save_data['email'] = rcube_idn_to_ascii($save_data['email']);
if ($save_data['bcc'])
$save_data['bcc'] = rcube_idn_to_ascii($save_data['bcc']);
if ($save_data['reply-to'])
$save_data['reply-to'] = rcube_idn_to_ascii($save_data['reply-to']);
if (!$plugin['abort'])
- $insert_id = $save_data['email'] ? $USER->insert_identity($save_data) : null;
+ $insert_id = $save_data['email'] ? $RCMAIL->user->insert_identity($save_data) : null;
else
$insert_id = $plugin['result'];
if ($insert_id) {
$OUTPUT->show_message('successfullysaved', 'confirmation', null, false);
$_GET['_iid'] = $insert_id;
if (!empty($_POST['_standard']))
$default_id = $insert_id;
}
else {
// show error message
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error', null, false);
rcmail_overwrite_action('edit-identity');
return;
}
}
else
$OUTPUT->show_message('opnotpermitted', 'error');
// mark all other identities as 'not-default'
if ($default_id)
- $USER->set_default($default_id);
+ $RCMAIL->user->set_default($default_id);
// go to next step
rcmail_overwrite_action('identities');
diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc
index a6388c579..5cb79d160 100644
--- a/program/steps/settings/save_prefs.inc
+++ b/program/steps/settings/save_prefs.inc
@@ -1,202 +1,202 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/settings/save_prefs.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Save user preferences to DB and to the current session |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
$CURR_SECTION = get_input_value('_section', RCUBE_INPUT_POST);
$a_user_prefs = array();
// set options for specified section
switch ($CURR_SECTION)
{
case 'general':
$a_user_prefs = array(
'language' => isset($_POST['_language']) ? get_input_value('_language', RCUBE_INPUT_POST) : $CONFIG['language'],
'timezone' => isset($_POST['_timezone']) ? (is_numeric($_POST['_timezone']) ? floatval($_POST['_timezone']) : get_input_value('_timezone', RCUBE_INPUT_POST)) : $CONFIG['timezone'],
'dst_active' => isset($_POST['_dst_active']) ? TRUE : FALSE,
'pagesize' => is_numeric($_POST['_pagesize']) ? max(2, intval($_POST['_pagesize'])) : $CONFIG['pagesize'],
'date_format' => isset($_POST['_date_format']) ? get_input_value('_date_format', RCUBE_INPUT_POST) : $CONFIG['date_format'],
'time_format' => isset($_POST['_time_format']) ? get_input_value('_time_format', RCUBE_INPUT_POST) : ($CONFIG['time_format'] ? $CONFIG['time_format'] : 'H:i'),
'prettydate' => isset($_POST['_pretty_date']) ? TRUE : FALSE,
'skin' => isset($_POST['_skin']) ? get_input_value('_skin', RCUBE_INPUT_POST) : $CONFIG['skin'],
);
// compose derived date/time format strings
if ((isset($_POST['_date_format']) || isset($_POST['_time_format'])) && $a_user_prefs['date_format'] && $a_user_prefs['time_format']) {
$a_user_prefs['date_short'] = 'D ' . $a_user_prefs['time_format'];
$a_user_prefs['date_long'] = $a_user_prefs['date_format'] . ' ' . $a_user_prefs['time_format'];
}
break;
case 'mailbox':
$a_user_prefs = array(
'preview_pane' => isset($_POST['_preview_pane']) ? TRUE : FALSE,
'preview_pane_mark_read' => isset($_POST['_preview_pane_mark_read']) ? intval($_POST['_preview_pane_mark_read']) : $CONFIG['preview_pane_mark_read'],
'autoexpand_threads' => isset($_POST['_autoexpand_threads']) ? intval($_POST['_autoexpand_threads']) : 0,
'mdn_requests' => isset($_POST['_mdn_requests']) ? intval($_POST['_mdn_requests']) : 0,
'keep_alive' => isset($_POST['_keep_alive']) ? intval($_POST['_keep_alive'])*60 : $CONFIG['keep_alive'],
'check_all_folders' => isset($_POST['_check_all_folders']) ? TRUE : FALSE,
);
break;
case 'mailview':
$a_user_prefs = array(
'prefer_html' => isset($_POST['_prefer_html']) ? TRUE : FALSE,
'inline_images' => isset($_POST['_inline_images']) ? TRUE : FALSE,
'show_images' => isset($_POST['_show_images']) ? intval($_POST['_show_images']) : 0,
'display_next' => isset($_POST['_display_next']) ? TRUE : FALSE,
'default_charset' => get_input_value('_default_charset', RCUBE_INPUT_POST),
);
break;
case 'compose':
$a_user_prefs = array(
'htmleditor' => intval($_POST['_htmleditor']),
'draft_autosave' => isset($_POST['_draft_autosave']) ? intval($_POST['_draft_autosave']) : 0,
'mime_param_folding' => isset($_POST['_mime_param_folding']) ? intval($_POST['_mime_param_folding']) : 0,
'force_7bit' => isset($_POST['_force_7bit']) ? TRUE : FALSE,
'mdn_default' => isset($_POST['_mdn_default']) ? TRUE : FALSE,
'dsn_default' => isset($_POST['_dsn_default']) ? TRUE : FALSE,
'reply_same_folder' => isset($_POST['_reply_same_folder']) ? TRUE : FALSE,
'spellcheck_before_send' => isset($_POST['_spellcheck_before_send']) ? TRUE : FALSE,
'spellcheck_ignore_syms' => isset($_POST['_spellcheck_ignore_syms']) ? TRUE : FALSE,
'spellcheck_ignore_nums' => isset($_POST['_spellcheck_ignore_nums']) ? TRUE : FALSE,
'spellcheck_ignore_caps' => isset($_POST['_spellcheck_ignore_caps']) ? TRUE : FALSE,
'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1,
'top_posting' => !empty($_POST['_top_posting']),
'strip_existing_sig' => isset($_POST['_strip_existing_sig']),
'sig_above' => !empty($_POST['_sig_above']) && !empty($_POST['_top_posting']),
'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST),
);
break;
case 'addressbook':
$a_user_prefs = array(
'default_addressbook' => get_input_value('_default_addressbook', RCUBE_INPUT_POST, true),
'autocomplete_single' => isset($_POST['_autocomplete_single']) ? TRUE : FALSE,
);
break;
case 'server':
$a_user_prefs = array(
'read_when_deleted' => isset($_POST['_read_when_deleted']) ? TRUE : FALSE,
'skip_deleted' => isset($_POST['_skip_deleted']) ? TRUE : FALSE,
'flag_for_deletion' => isset($_POST['_flag_for_deletion']) ? TRUE : FALSE,
'delete_always' => isset($_POST['_delete_always']) ? TRUE : FALSE,
'logout_purge' => isset($_POST['_logout_purge']) ? TRUE : FALSE,
'logout_expunge' => isset($_POST['_logout_expunge']) ? TRUE : FALSE,
);
break;
case 'folders':
$a_user_prefs = array(
'drafts_mbox' => get_input_value('_drafts_mbox', RCUBE_INPUT_POST, true),
'sent_mbox' => get_input_value('_sent_mbox', RCUBE_INPUT_POST, true),
'junk_mbox' => get_input_value('_junk_mbox', RCUBE_INPUT_POST, true),
'trash_mbox' => get_input_value('_trash_mbox', RCUBE_INPUT_POST, true),
);
break;
}
$plugin = rcmail::get_instance()->plugins->exec_hook('preferences_save',
array('prefs' => $a_user_prefs, 'section' => $CURR_SECTION));
$a_user_prefs = $plugin['prefs'];
// don't override these parameters
foreach ((array)$CONFIG['dont_override'] as $p)
$a_user_prefs[$p] = $CONFIG[$p];
// verify some options
switch ($CURR_SECTION)
{
case 'general':
// switch UI language
if (isset($_POST['_language']) && $a_user_prefs['language'] != $_SESSION['language']) {
$RCMAIL->load_language($a_user_prefs['language']);
$OUTPUT->command('reload', 500);
}
// switch skin (if valid, otherwise unset the pref and fall back to default)
if (!$OUTPUT->set_skin($a_user_prefs['skin']))
unset($a_user_prefs['skin']);
else if ($RCMAIL->config->get('skin') != $a_user_prefs['skin'])
$OUTPUT->command('reload', 500);
// force min size
if ($a_user_prefs['pagesize'] < 1)
$a_user_prefs['pagesize'] = 10;
if (isset($CONFIG['max_pagesize']) && ($a_user_prefs['pagesize'] > $CONFIG['max_pagesize']))
$a_user_prefs['pagesize'] = (int) $CONFIG['max_pagesize'];
$a_user_prefs['timezone'] = (string) $a_user_prefs['timezone'];
break;
case 'mailbox':
// force keep_alive
if (isset($a_user_prefs['keep_alive'])) {
$a_user_prefs['keep_alive'] = max(60, $CONFIG['min_keep_alive'], $a_user_prefs['keep_alive']);
if (!empty($CONFIG['session_lifetime']))
$a_user_prefs['keep_alive'] = min($CONFIG['session_lifetime']*60, $a_user_prefs['keep_alive']);
}
break;
case 'folders':
// special handling for 'default_imap_folders'
if (in_array('default_imap_folders', (array)$CONFIG['dont_override'])) {
foreach (array('drafts_mbox','sent_mbox','junk_mbox','trash_mbox') as $p)
$a_user_prefs[$p] = $CONFIG[$p];
} else {
$a_user_prefs['default_imap_folders'] = array('INBOX');
foreach (array('drafts_mbox','sent_mbox','junk_mbox','trash_mbox') as $p) {
if ($a_user_prefs[$p])
$a_user_prefs['default_imap_folders'][] = $a_user_prefs[$p];
}
}
break;
}
// Save preferences
if (!$plugin['abort'])
- $saved = $USER->save_prefs($a_user_prefs);
+ $saved = $RCMAIL->user->save_prefs($a_user_prefs);
else
$saved = $plugin['result'];
if ($saved)
$OUTPUT->show_message('successfullysaved', 'confirmation');
else
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error');
// display the form again
rcmail_overwrite_action('edit-prefs');

File Metadata

Mime Type
text/x-diff
Expires
Fri, Aug 21, 3:10 AM (5 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1273181
Default Alt Text
(260 KB)

Event Timeline