--- /dev/null
+# AddDefaultCharset UTF-8
+php_flag display_errors Off
+php_flag log_errors On
+php_value error_log logs/errors
+php_value upload_max_filesize 2M
+
+<FilesMatch "(\.inc|\~)$|^_">
+ Order allow,deny
+ Deny from all
+</FilesMatch>
+
+Order deny,allow
+Allow from all
--- /dev/null
+CHANGELOG RoundCube Webmail
+---------------------------
+
+2007/01/15 (thomasb)
+----------
+- Applied path for wrong HTML message parsing
+
+
+2006/12/22 (thomasb)
+----------
+- Applied security fixes for XSS vulnerabilites
+
+
+2006/08/04 (thomasb)
+----------
+- Fixed Bug in saving identities (Ticket #1483915)
+- Set folder name in window title (Bug #1483919)
+- Don't add imap_root to INBOX path (Bug #1483816)
+- Attempt to create default folders only after login
+- Avoid usage of $CONFIG in rcube_imap class
+
+
+2006/07/30 (thomasb)
+----------
+- Alter links in HTML messages (Bug #1326402)
+- Added fallback if host not found in 'mail_domain' array
+- Applied patch of Charles to highlight droptargets (Ticket #1473034)
+- Fixed folder renaming (Bug #1483914)
+- Added confirmation message after deleting a folder
+
+
+2006/07/25 (thomasb)
+----------
+- Made folder renaming a bit more ajax-style
+- Changed rename-labels and German translation
+- Fixed addressbox countbar width (Bug #1483845)
+- Fixed refresh interval problems in Safari (Bug #1483902)
+- Fixed clear_message_list_header() errors (Bug #1483898)
+- Sanity check of $message_set in imap.inc (Bug #1443200)
+- Added correct changing of message list headers for Sent folder
+- Updated Spanish localization (Ticket #1483887)
+- Applied patch #1483846
+
+
+2006/07/24 (richs)
+----------
+- Draft window no longer reloads. It saves to an iframe in the background instead (fixes bug #1483869)
+- Draft timer now part of program/js/app.js instead of skins/default/templates/compose.inc
+- Draft saving now properly returns an error when saving fails
+- Draft timer stops and resets properly when attachments are uploaded, or when saving manually
+- Old compose session/attachments are now cleaned up when a new/forward/reply/draft is made/opened
+
+
+2006/07/19 (thomasb)
+----------
+- Correct entity encoding of link urls (HTML validity)
+- Improved usability in compose step (Ticket #1483807)
+- Added absolute URLs to several buttons (for "open in new window")
+- Applied patch #1328032
+- Fixed Bug/Patch #1443200
+
+
+2006/07/18 (thomasb)
+----------
+- Fixed password with spaces issue (Bug #1364122)
+- Replaced _auth hash with second cookie (Ticket #1483811)
+- Don't use get_input_value() for passwords (Bug #1468895)
+- Made password encryption key configurable
+- Minor bugfixes with charset encoding
+- Added <label> tags to forms (Ticket #1483810)
+
+
+2006/07/07 (thomasb)
+----------
+- Fixed INSTALL_PATH bug #1425663
+
+
+2006/07/03 (richs)
+----------
+- Fixed compatibility with in-body email addresses containing "+" (Bug #1483836)
+- Updated French localizations (Ticket #1483862)
+- Incoming messages can now be moved to Drafts, edited, saved, then moved back (Feature #1436191)
+- Added Firefox workaround when clicking whitespace to drag messages (Bug #1483857)
+- Corrected Dutch and Italian localizations (Ticket #1483851 and #1483848)
+- Enabled 'Empty' (purge) command for Junk mailbox (defined in main.inc.php)
+
+
+2006/06/30 (richs)
+----------
+- Fixed empty INBOX compatibility bug (Patch #1443200)
+- Temporarily fixed French "compact" localization (Patch #1483862)
+- Fixed "Select All" not working with Delete interface button (Bug #1332434)
+- Fixed messsage list column compatibility with Konqueror (Bug #1395711)
+- Fixed "unread count" in window title when count changed (Bug #1483812)
+- Fixed DB error when deleting from message table (Patch #1483835)
+
+
+2006/06/29 (richs)
+----------
+- Added ability to remove attachments (Feature #1436721)
+- Default folders are now auto-created on first login (Feature #1471594)
+- Fixed compatibility with folder apostrophes (e.g.: Joe's Folder) (Bug #1429458)
+- Corrected Italian localizations
+- Tweaked rename-folder form to clear after a rename
+
+
+2006/06/26 (richs)
+----------
+- Added button to immediately check for new messages
+- New message checking now displays status "Checking for new messages..."
+- New message checking now looks for unread messages in all mailboxes (Feature #1326401)
+- Task buttons now respond to clicks by darkening (as in other applications)
+- Fixed "Sender" column changing to "Recipient" for "Sent" and "Drafts" message lists
+- Added ability to sort messages by "Size"
+- Added ability to rename folders (Feature #1326396)
+- Added 'protect_default_folders' option to main.inc.php to prevent renames/deletes/unsubscribes of default folders
+- Corrected 5 typos of "INSTLL" to "INSTALL" in program/include/main.inc
+
+
+2006/06/25
+----------
+- Changed behavior to include host-specific configuration (Bug #1483849)
+- Assume ISO-8859-1 encoding of mail messages by default (Patch #1483839)
+- Fixed spell checker to work with the new URL at google.com
+- Some memory and security optimizations sendmail.inc
+- Updated UGRADING description
+
+
+2006/06/19
+----------
+- Added Drafts support (Feature #1326839) (richs)
+
+
+2006/06/02
+----------
+- Updated Estonian localization and moved from ee to et
+- Added Bulgarian localization
+
+
+2006/05/25
+----------
+- Finalized GoogieSpell integration
+
+
+2006/05/18
+----------
+- Added Arabic and Armenian localizations
+- Updated Russian localization
+- Removed MDB2 classes from repository. Install them seperately if used.
+- Updated MDB2 wrapper class contributed by Lukas Kahwe Smith
+- Allow & in e-mail addresses
+
+
+2006/05/05
+----------
+- Fixed typos in function rcube_button() (Bugs #1473198 and #1473201)
+- Check for zlib.output_compression before using ob_gzhandler (Bug #1471069)
+- Casting date parts in iil_StrToTime() to avoid warnings (Bug #1482140)
+- Corrected INSTALL description (Bug #1476106)
+- Added charset to javascript HTTP headers
+- Fixed Opera bug with CC and BCC fields (Bug #1474576)
+- Changed login page title regarding product name (Bug #1476413)
+- Pimped search function
+- Applied attachment viewing/forwarding patches by Andrew Fladmark
+- Applied prev/next patch by Leonard Bouchet
+- Applied patches by Mark Bucciarelli
+- Applied patch for requesting receipts by Salvatore Ansani
+- Integrated GoogieSpell as suggested by phil (styling is not perfect yet, localization is missing)
+
+
+2006/04/13
+----------
+- Added Slovenian localization
+- Updated Portuguese localization
+- Fixed parent.location problem for compose-links
+- Added sort order saving patch by Jacob Brunson
+- Added gzip compression support
+
+
+2006/04/02
+----------
+- Added Lithuanian localization
+- Improved search function
+- Added version string as template object
+- Load host-specific configuration file (see config/main.inc.php)
+- New config parameter adding domain to user names for login
+- Strip tags on _auth, _action, _task parameters
+- Corrected labels for next/previous page buttons in address book
+
+
+2006/03/23
+----------
+- Auto-detect mail header delimiters
+- Regard daylight savings
+- Localized quota display
+- Started implementing search function
+
+
+2006/03/20
+----------
+- Avoid error message when saving an unchanged identity (Bug #1429510)
+- Fixed hard-coded cols selection for sent folder (Bug #1354586)
+- Enable some HTML links for use with "open in new window" or "save target"
+- Check meta-key instead of ctrl on Macs
+- Ignore double clicks when holding down a modifier key
+- Fixed reloading of the login page
+- Fixed typo in compose template (Bug #1446852)
+- Added compose button to message read step (Request #1433288)
+- New config parameter for persistent database connections (Bug #1431817)
+
+
+2006/03/14
+----------
+- Don't remove internal HTML tags in plaintext messages
+- Improved error handling in DB connection failure
+
+
+2006/02/22
+----------
+- Updated localizations
+- Fixed bug #1435989
+
+
+2006/02/19
+----------
+- Updated localizations
+- Applied patch of Anders Karlsson
+- Applied patch of Jacob Brunson
+- Applied patch for virtuser_query by Robin Elfrink
+- Added support for References header (patch by Auke)
+- Added support for mbstring module by Tadashi Jokagi
+- Added function for automatic remove of slashes on GET and POST vars
+ if magic_quotes is enabled
+
+
+2006/02/05
+----------
+- Added Slovak, Hungarian, Bosnian and Croation translation
+- Fixed bug when inserting signatures with !?&
+- Chopping message headers before inserting into the message cache table
+ (to avoid bugs in Postgres)
+- Allow one-char domains in e-mail addresses
+- Make product name in page title configurable
+- Make username available as skin object
+- Added session_write_close() in rcube_db class destructor to avoid problems
+ in PHP 5.0.5
+- Use move_uploaded_file() instead of copy() for a more secure handling of
+ uploaded attachments
+- Additional config parameter to show/hide deleted messages
+- Added periodic request for checking new mails (Request #1307821)
+- Added EXPUNGE command
+- Optimized loading time for mail interface
+- Changed to full UTF-8 support
+- Additional timezones (Patch #1389912)
+- Added LDAP public search (experimental)
+- Applied patch for correct ctrl/shift behavior for message selection (Bug #1326364)
+- Casting to strings when adding empty headers to message cache (Bug #1406026)
+- Skip sender-address as recipient when Reply-to-all
+- Fixes in utf8-class
+- Added patch for Quota display by Aury Fink Filho <nuny@aury.com.br>
+- Added garbage collector for message cache
+- Added patches for BCC headers
+
+
+2005/12/16
+----------
+- Added Turkish and Simplified Chinese translation
+- Use virtusertable to resolve e-mail addresses at login
+- Configurable mail_domain used to compose correct e-mail addresses
+ on first login
+
+
+2005/12/03
+----------
+- Added Finnish, Romanian, Polish, Czech, British, Norwegian, Greek, Russian,
+ Estonian and Chinese translation
+- Get IMAP server capabilities in array
+- Check for NAMESPACE capability before sending command
+- Set default user language from config 'locale_string'
+- Added sorting patch for message list
+- Make default sort col/order configurable
+- Fixed XSS in address book and identities
+- Added more XSS protection (Bug #1308236)
+- Added tab indexes for compose form
+- Added 'changed' col to contacts table
+- Support for 160-bit session hashes
+- Added input check for contacts and identities (Patch #1346523)
+- Added messages/warning to compose step (Patch #1323895)
+- Added favicon to the default skin
+- Fixed Bug #1334337 as far as possible
+- Added Reply-To-All functionality (Request #1326395, Patch #1349777)
+- Redesign of client side AJAX code (enable multi threading)
+- Added keep-alive signal every minute
+- Make logs dir configurable
+- Added support for SMTPS
+- Decode attachment file names
+- Make delimiter for message headers configurable
+- Add generic footer to sent messages
+- Choose the rigt identity when replying
+- Remove signature when replying (Request #1333167)
+- Signatures for each identity
+- Select charset when composing message
+- Complete re-design of the caching mechanism
+
+
+2005/08/11
+----------
+- Write list header to client even if list is empty
+- Add functions "select all", "select none" to message list
+- Improved filter for HTML messages to remove potentially malicious tags
+ (script, iframe, object) and event handlers.
+- Buttons for next/previous message in view mode
+- Add new created contact to list and show confirmation status
+- Added folder management (subscribe/create/delete)
+- Log message sending (SMTP log)
+- Grant access for Camino browser
+- Added German translation
+
+
+2005/10/20
+----------
+- Added Swedish, Latvian, Portuguese and Catalan translation
+- Make SMTP auth method configurable
+- Make mailboxlist scrollable (Bug #1326372)
+- Fixed SSL support
+- Improved support for Courier IMAP (root folder and delimiter issues)
+- Moved taskbar from bottom to top
+- Added 'session_lifetime' parameter
+- Fixed wrong unread count when deleting message (Bug #1332434)
+- Srip tags when creating a new folder (Bug #1332084)
+- Translate HTML tags in message headers (Bug #1330134)
+- Correction in German translation (Bug #1329434)
+- Display folder names with special chars correctly (Bug #1330157)
+
+
+2005/10/07
+----------
+- Added French, Italian, Spanish, Danish, Dutch translation
+- Clarified license (Bug #1305966)
+- Fixed PHP warnings (Bug #1299403)
+- Fixed english translation (Bug #1295406)
+- Fixed bug #1290833: Last character of email not seen
+- Fixed bug #1292199 when creating new user
+- Allow more browsers (Bug #1285101)
+- Added setting for showing pretty dates
+- Added support for SQLite database
+- Make use of message caching configurable
+- Also add attachments when forwarding a message
+- Long folder names will not flow over message list (Bug #1267232)
+- Show nested mailboxes hieracically
+- Enable IMAPS by host
+
+
+2005/08/20
+----------
+- Improved cacheing of mailbox messagecount
+- Fixed javascript bug when creating a new message folder
+- Fixed javascript bugs #1260990 and #1260992: folder selection
+- Make Trash folder configurable
+- Auto create folders Inbox, Sent and Trash (if configured)
+- Support for IMAP root folder
+- Added support fot text/enriched messages
+- Make list of special mailboxes configurable
+
--- /dev/null
+
+REQUIREMENTS
+============
+
+* The Apache Webserver
+* .htaccess support allowing overrides for DirectoryIndex
+* PHP Version 4.3.1 or greater
+* PCRE (perl compatible regular expression) installed with PHP
+* php.ini options:
+ - error_reporting E_ALL & ~E_NOTICE (or lower)
+ - file_uploads on (for attachment upload features)
+ - memory_limit (increase as suitable to support large attachments)
+* A MySQL or PostgreSQL database engine or the SQLite extension for PHP
+* A database with permission to create tables
+
+
+INSTALLATION
+============
+
+1. Decompress and put this folder somewhere inside your document root
+2. Make sure that the following directories (and the files within)
+ are writable by the webserver
+ - /temp
+ - /logs
+3. Create a new database and a database user for RoundCube (see DATABASE SETUP)
+4. Create database tables using the queries in file 'SQL/*.initial.sql'
+ (* stands for your database type)
+5. Rename the files config/*.inc.php.dist to config/*.inc.php
+6. Modify the files in config/* to suit your local environment
+7. Done!
+
+
+DATABASE SETUP
+==============
+
+* MySQL 4.0.x
+-------------
+Setting up the mysql database can be done by creating an empty database,
+importing the table layout and granting the proper permissions to the
+roundcube user. Here is an example of that procedure:
+
+# mysql
+> CREATE DATABASE 'roundcubemail';
+> GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost
+ IDENTIFIED BY 'password';
+> quit
+# mysql roundcubemail < SQL/mysql.initial.sql
+
+
+* MySQL 4.1.x/5.x
+-----------------
+For MySQL version 4.1 and up, it's recommended to create the database for
+RoundCube with utf-8 charset. Here's an example of the init procedure:
+
+# mysql
+> CREATE DATABASE 'roundcubemail' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
+> GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost
+ IDENTIFIED BY 'password';
+> quit
+
+# mysql roundcubemail < SQL/mysql5.initial.sql
+
+
+* SQLite
+--------
+Sqlite requires specifically php5 (sqlite in php4 currently doesn't
+work with roundcube), and you need sqlite 2 (preferably 2.8) to setup
+the sqlite db (sqlite 3.x also doesn't work at the moment). Here is
+an example how you can setup the sqlite.db for roundcube:
+
+# sqlite -init SQL/sqlite.initial.sql sqlite.db
+
+Make sure your configuration points to the sqlite.db file and that the
+webserver can write to the file.
+
+
+* PostgreSQL
+------------
+To use RoundCube with PostgreSQL support you have to follow the next
+simple steps, which have to be done with the postgres system user (or
+which ever is the database superuser):
+
+$ createuser roundcubemail
+$ createdb -O roundcubemail roundcubemail
+$ psql roundcubemail
+
+roundcubemail =# ALTER USER roundcube WITH PASSWORD 'the_new_password';
+roundcubemail =# \c - roundcubemail
+roundcubemail => \i SQL/postgres.initial.sql
+
+All this has been tested with PostgreSQL 8.0.x and 7.4.x. Older
+versions don't have a -O option for the createdb, so if you are
+using that version you'll have to change ownership of the DB later.
+
+
+CONFIGURATION
+=============
+
+Change the files in config/* according your to environment and your needs.
+Details about the config paramaters can be found in the config files.
+
+
+UPGRADING
+=========
+If you already have a previous version of RoundCube installed,
+please refer to the instructions in UPGRADING guide.
+
+
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
--- /dev/null
+RoundCube Webmail (http://roundcube.net)
+
+
+ATTENTION
+---------
+This is just a snapshot of the current CVS repository and is NOT A STABLE
+version of RoundCube. There have been major changes since the latest release
+so please read the update instructions carefully. It's not recommended to
+replace an existing installation of RoundCube with this version. Also using
+a separate database or this installation is highly recommended.
+
+
+Introduction:
+-------------
+RoundCube Webmail is a browser-based multilingual IMAP client with an
+application-like user interface. It provides full functionality you expect from
+an e-mail client, including MIME support, address book, folder manipulation and
+message filters. RoundCube Webmail is written in PHP and requires the MySQL
+database. The user interface is fully skinnable using XHTML and CSS 2.
+
+This project is meant to be a modern webmail solution which is easy to
+install/configure and that runs on a standard PHP plus MySQL or Postgres
+configuration. It includes open-source classes/libraries like PEAR
+(http://pear.php.net) and the IMAP wrapper from IlohaMail
+(http://www.ilohamail.org).
+
+The current development skin uses icons designed by Stephen Horlander and Kevin
+Gerich for Mozilla.org.
+
+
+How it works:
+-------------
+The main authority for the RoundCube access is the IMAP server. If
+'auto_create_user' is set to TRUE in config/main.inc.php a new record in the
+user table will be created once the IMAP login succeeded. This user record does
+not store a password, it's just used to assign identities, contacts and cache
+records. If you have 'auto_create_user' set to FALSE only IMAP logins which
+already have a corresponding entry in the user's table (username and hostname)
+will be allowed.
+
+
+Code Layout:
+------------
+
+Basic sequence (index.php):
+ - index.php -> load_gui -> parse_template
+ - authentication details in this sequence
+
+Tasks
+ - index.php limits tasks to set list
+ - can see task in roundcube link when you mouse over it
+ - task templates stored in skins/default/templates
+ - templates "roundcube:" tokens that get replaced in parse_template
+
+program/include/rcube_shared.inc
+ - defines rcube_html_page, class that lays out a roundcube web page
+ - defines form control classes
+
+
+Installation:
+-------------
+For detailed instructions on how to install RoundCube webmail on your server,
+please refer to the INSTALL document in the same directory as this document.
+
+
+Licensing:
+----------
+This product is distributed under the GPL. Please read through the file
+LICENSE for more information about our license.
+
+
+Contact:
+--------
+For any bug reports or feature requests please refer to the tracking system
+at sourceforge.net (http://sourceforge.net/tracker/?group_id=139281) or
+subscribe to our mailing list. See http://www.roundcube.net/?p=mailinglists
+for details.
+
+You're always welcome to send a message to the project admin:
+roundcube@gmail.com
+
+
--- /dev/null
+-- RoundCube Webmail initial database structure
+-- Version 0.1beta2
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `cache`
+--
+
+CREATE TABLE `cache` (
+ `cache_id` int(10) unsigned NOT NULL auto_increment,
+ `user_id` int(10) unsigned NOT NULL default '0',
+ `session_id` varchar(40) default NULL,
+ `cache_key` varchar(128) NOT NULL default '',
+ `created` datetime NOT NULL default '0000-00-00 00:00:00',
+ `data` longtext NOT NULL,
+ PRIMARY KEY (`cache_id`),
+ KEY `user_id` (`user_id`),
+ KEY `cache_key` (`cache_key`),
+ KEY `session_id` (`session_id`)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `contacts`
+--
+
+CREATE TABLE `contacts` (
+ `contact_id` int(10) unsigned NOT NULL auto_increment,
+ `user_id` int(10) unsigned NOT NULL default '0',
+ `changed` datetime NOT NULL default '0000-00-00 00:00:00',
+ `del` tinyint(1) NOT NULL default '0',
+ `name` varchar(128) NOT NULL default '',
+ `email` varchar(128) NOT NULL default '',
+ `firstname` varchar(128) NOT NULL default '',
+ `surname` varchar(128) NOT NULL default '',
+ `vcard` text NOT NULL,
+ PRIMARY KEY (`contact_id`),
+ KEY `user_id` (`user_id`)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `identities`
+--
+
+CREATE TABLE `identities` (
+ `identity_id` int(10) unsigned NOT NULL auto_increment,
+ `user_id` int(10) unsigned NOT NULL default '0',
+ `del` tinyint(1) NOT NULL default '0',
+ `standard` tinyint(1) NOT NULL default '0',
+ `name` varchar(128) NOT NULL default '',
+ `organization` varchar(128) NOT NULL default '',
+ `email` varchar(128) NOT NULL default '',
+ `reply-to` varchar(128) NOT NULL default '',
+ `bcc` varchar(128) NOT NULL default '',
+ `signature` text NOT NULL,
+ PRIMARY KEY (`identity_id`),
+ KEY `user_id` (`user_id`)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `session`
+--
+
+CREATE TABLE `session` (
+ `sess_id` varchar(40) NOT NULL default '',
+ `created` datetime NOT NULL default '0000-00-00 00:00:00',
+ `changed` datetime NOT NULL default '0000-00-00 00:00:00',
+ `ip` VARCHAR(15) NOT NULL default '',
+ `vars` text NOT NULL,
+ PRIMARY KEY (`sess_id`)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `users`
+--
+
+CREATE TABLE `users` (
+ `user_id` int(10) unsigned NOT NULL auto_increment,
+ `username` varchar(128) NOT NULL default '',
+ `mail_host` varchar(128) NOT NULL default '',
+ `alias` varchar(128) NOT NULL default '',
+ `created` datetime NOT NULL default '0000-00-00 00:00:00',
+ `last_login` datetime NOT NULL default '0000-00-00 00:00:00',
+ `language` varchar(5) NOT NULL default 'en',
+ `preferences` text NOT NULL default '',
+ PRIMARY KEY (`user_id`)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `messages`
+--
+
+CREATE TABLE `messages` (
+ `message_id` int(11) unsigned NOT NULL auto_increment,
+ `user_id` int(11) unsigned NOT NULL default '0',
+ `del` tinyint(1) NOT NULL default '0',
+ `cache_key` varchar(128) NOT NULL default '',
+ `created` datetime NOT NULL default '0000-00-00 00:00:00',
+ `idx` int(11) unsigned NOT NULL default '0',
+ `uid` int(11) unsigned NOT NULL default '0',
+ `subject` varchar(255) NOT NULL default '',
+ `from` varchar(255) NOT NULL default '',
+ `to` varchar(255) NOT NULL default '',
+ `cc` varchar(255) NOT NULL default '',
+ `date` datetime NOT NULL default '0000-00-00 00:00:00',
+ `size` int(11) unsigned NOT NULL default '0',
+ `headers` text NOT NULL,
+ `body` longtext,
+ PRIMARY KEY (`message_id`),
+ KEY `user_id` (`user_id`),
+ KEY `cache_key` (`cache_key`),
+ KEY `idx` (`idx`),
+ KEY `uid` (`uid`)
+);
+
+
--- /dev/null
+-- RoundCube Webmail update script for MySQL databases
+-- Updates from version 0.1-20051007
+
+
+ALTER TABLE `session` ADD `ip` VARCHAR(15) NOT NULL AFTER changed;
+ALTER TABLE `users` ADD `alias` VARCHAR(128) NOT NULL AFTER mail_host;
+
+
+
+-- RoundCube Webmail update script for MySQL databases
+-- Updates from version 0.1-20051021
+
+ALTER TABLE `session` CHANGE `sess_id` `sess_id` VARCHAR(40) NOT NULL;
+
+ALTER TABLE `contacts` CHANGE `del` `del` TINYINT(1) NOT NULL;
+ALTER TABLE `contacts` ADD `changed` DATETIME NOT NULL AFTER `user_id`;
+
+UPDATE `contacts` SET `del`=0 WHERE `del`=1;
+UPDATE `contacts` SET `del`=1 WHERE `del`=2;
+
+ALTER TABLE `identities` CHANGE `default` `standard` TINYINT(1) NOT NULL;
+ALTER TABLE `identities` CHANGE `del` `del` TINYINT(1) NOT NULL;
+
+UPDATE `identities` SET `del`=0 WHERE `del`=1;
+UPDATE `identities` SET `del`=1 WHERE `del`=2;
+UPDATE `identities` SET `standard`=0 WHERE `standard`=1;
+UPDATE `identities` SET `standard`=1 WHERE `standard`=2;
+
+CREATE TABLE `messages` (
+ `message_id` int(11) unsigned NOT NULL auto_increment,
+ `user_id` int(11) unsigned NOT NULL default '0',
+ `del` tinyint(1) NOT NULL default '0',
+ `cache_key` varchar(128) NOT NULL default '',
+ `created` datetime NOT NULL default '0000-00-00 00:00:00',
+ `idx` int(11) unsigned NOT NULL default '0',
+ `uid` int(11) unsigned NOT NULL default '0',
+ `subject` varchar(255) NOT NULL default '',
+ `from` varchar(255) NOT NULL default '',
+ `to` varchar(255) NOT NULL default '',
+ `cc` varchar(255) NOT NULL default '',
+ `date` datetime NOT NULL default '0000-00-00 00:00:00',
+ `size` int(11) unsigned NOT NULL default '0',
+ `headers` text NOT NULL,
+ `body` longtext,
+ PRIMARY KEY (`message_id`),
+ KEY `user_id` (`user_id`),
+ KEY `cache_key` (`cache_key`),
+ KEY `idx` (`idx`),
+ KEY `uid` (`uid`)
+) TYPE=MyISAM;
+
+
+
+-- RoundCube Webmail update script for MySQL databases
+-- Updates from version 0.1-20051216
+
+ALTER TABLE `messages` ADD `created` DATETIME NOT NULL AFTER `cache_key` ;
--- /dev/null
+-- RoundCube Webmail initial database structure
+-- Version 0.1beta2
+--
+
+-- --------------------------------------------------------
+
+SET FOREIGN_KEY_CHECKS=0;
+
+
+-- Table structure for table `session`
+
+CREATE TABLE `session` (
+ `sess_id` varchar(40) NOT NULL,
+ `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `changed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `ip` varchar(15) NOT NULL,
+ `vars` text NOT NULL,
+ PRIMARY KEY(`sess_id`)
+) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+
+-- Table structure for table `users`
+
+CREATE TABLE `users` (
+ `user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `username` varchar(128) NOT NULL,
+ `mail_host` varchar(128) NOT NULL,
+ `alias` varchar(128) NOT NULL,
+ `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `last_login` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `language` varchar(5) NOT NULL DEFAULT 'en',
+ `preferences` text NOT NULL,
+ PRIMARY KEY(`user_id`)
+) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+
+-- Table structure for table `messages`
+
+CREATE TABLE `messages` (
+ `message_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `del` tinyint(1) NOT NULL DEFAULT '0',
+ `cache_key` varchar(128) NOT NULL,
+ `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `idx` int(11) UNSIGNED NOT NULL DEFAULT '0',
+ `uid` int(11) UNSIGNED NOT NULL DEFAULT '0',
+ `subject` varchar(255) NOT NULL,
+ `from` varchar(255) NOT NULL,
+ `to` varchar(255) NOT NULL,
+ `cc` varchar(255) NOT NULL,
+ `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `size` int(11) UNSIGNED NOT NULL DEFAULT '0',
+ `headers` text NOT NULL,
+ `body` longtext,
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY(`message_id`),
+ INDEX `cache_key`(`cache_key`),
+ INDEX `idx`(`idx`),
+ INDEX `uid`(`uid`),
+ CONSTRAINT `User_ID_FK_messages` FOREIGN KEY (`user_id`)
+ REFERENCES `users`(`user_id`)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+
+-- Table structure for table `cache`
+
+CREATE TABLE `cache` (
+ `cache_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `session_id` varchar(40),
+ `cache_key` varchar(128) NOT NULL,
+ `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `data` longtext NOT NULL,
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY(`cache_id`),
+ INDEX `cache_key`(`cache_key`),
+ INDEX `session_id`(`session_id`),
+ CONSTRAINT `User_ID_FK_cache` FOREIGN KEY (`user_id`)
+ REFERENCES `users`(`user_id`)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+
+-- Table structure for table `contacts`
+
+CREATE TABLE `contacts` (
+ `contact_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `changed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `del` tinyint(1) NOT NULL DEFAULT '0',
+ `name` varchar(128) NOT NULL,
+ `email` varchar(128) NOT NULL,
+ `firstname` varchar(128) NOT NULL,
+ `surname` varchar(128) NOT NULL,
+ `vcard` text NOT NULL,
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY(`contact_id`),
+ CONSTRAINT `User_ID_FK_contacts` FOREIGN KEY (`user_id`)
+ REFERENCES `users`(`user_id`)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+
+-- Table structure for table `identities`
+
+CREATE TABLE `identities` (
+ `identity_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `del` tinyint(1) NOT NULL DEFAULT '0',
+ `standard` tinyint(1) NOT NULL DEFAULT '0',
+ `name` varchar(128) NOT NULL,
+ `organization` varchar(128) NOT NULL,
+ `email` varchar(128) NOT NULL,
+ `reply-to` varchar(128) NOT NULL,
+ `bcc` varchar(128) NOT NULL,
+ `signature` text NOT NULL,
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY(`identity_id`),
+ CONSTRAINT `User_ID_FK_identities` FOREIGN KEY (`user_id`)
+ REFERENCES `users`(`user_id`)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+
+SET FOREIGN_KEY_CHECKS=1;
\ No newline at end of file
--- /dev/null
+--
+-- Sequence "user_ids"
+-- Name: user_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE user_ids
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+--
+-- Table "users"
+-- Name: users; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE users (
+ user_id integer DEFAULT nextval('user_ids'::text) PRIMARY KEY,
+ username character varying(128) DEFAULT ''::character varying NOT NULL,
+ mail_host character varying(128) DEFAULT ''::character varying NOT NULL,
+ alias character varying(128) DEFAULT ''::character varying NOT NULL,
+ created timestamp with time zone DEFAULT now() NOT NULL,
+ last_login timestamp with time zone DEFAULT now() NOT NULL,
+ "language" character varying(5) DEFAULT 'en'::character varying NOT NULL,
+ preferences text DEFAULT ''::text NOT NULL
+);
+
+
+
+--
+-- Table "session"
+-- Name: session; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE "session" (
+ sess_id character varying(40) DEFAULT ''::character varying PRIMARY KEY,
+ created timestamp with time zone DEFAULT now() NOT NULL,
+ changed timestamp with time zone DEFAULT now() NOT NULL,
+ ip character varying(16) NOT NULL,
+ vars text NOT NULL
+);
+
+
+
+--
+-- Sequence "identity_ids"
+-- Name: identity_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE identity_ids
+ START WITH 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+--
+-- Table "identities"
+-- Name: identities; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE identities (
+ identity_id integer DEFAULT nextval('identity_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
+ del integer DEFAULT 0 NOT NULL,
+ standard integer DEFAULT 0 NOT NULL,
+ name character varying(128) NOT NULL,
+ organization character varying(128),
+ email character varying(128) NOT NULL,
+ "reply-to" character varying(128),
+ bcc character varying(128),
+ signature text
+);
+
+
+
+--
+-- Sequence "contact_ids"
+-- Name: contact_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE contact_ids
+ START WITH 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+--
+-- Table "contacts"
+-- Name: contacts; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE contacts (
+ contact_id integer DEFAULT nextval('contact_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
+ changed timestamp with time zone DEFAULT now() NOT NULL,
+ del integer DEFAULT 0 NOT NULL,
+ name character varying(128) DEFAULT ''::character varying NOT NULL,
+ email character varying(128) DEFAULT ''::character varying NOT NULL,
+ firstname character varying(128) DEFAULT ''::character varying NOT NULL,
+ surname character varying(128) DEFAULT ''::character varying NOT NULL,
+ vcard text
+);
+
+
+
+--
+-- Sequence "cache_ids"
+-- Name: cache_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE cache_ids
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+--
+-- Table "cache"
+-- Name: cache; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE "cache" (
+ cache_id integer DEFAULT nextval('cache_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
+ session_id character varying(40) REFERENCES "session" (sess_id),
+ cache_key character varying(128) DEFAULT ''::character varying NOT NULL,
+ created timestamp with time zone DEFAULT now() NOT NULL,
+ data text NOT NULL
+);
+
+
+
+--
+-- Sequence "message_ids"
+-- Name: message_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE message_ids
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+--
+-- Table "messages"
+-- Name: messages; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE "messages" (
+ message_id integer DEFAULT nextval('message_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
+ del integer DEFAULT 0 NOT NULL,
+ cache_key character varying(128) DEFAULT ''::character varying NOT NULL,
+ created timestamp with time zone DEFAULT now() NOT NULL,
+ idx integer DEFAULT 0 NOT NULL,
+ uid integer DEFAULT 0 NOT NULL,
+ subject character varying(128) DEFAULT ''::character varying NOT NULL,
+ "from" character varying(128) DEFAULT ''::character varying NOT NULL,
+ "to" character varying(128) DEFAULT ''::character varying NOT NULL,
+ cc character varying(128) DEFAULT ''::character varying NOT NULL,
+ date timestamp with time zone NOT NULL,
+ size integer DEFAULT 0 NOT NULL,
+ headers text NOT NULL,
+ body text
+);
+
--- /dev/null
+-- RoundCube Webmail initial database structure
+-- Version 0.1a
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `cache`
+--
+
+CREATE TABLE cache (
+ cache_id integer NOT NULL PRIMARY KEY,
+ user_id integer NOT NULL default 0,
+ session_id varchar(40) default NULL,
+ cache_key varchar(128) NOT NULL default '',
+ created datetime NOT NULL default '0000-00-00 00:00:00',
+ data longtext NOT NULL
+);
+
+CREATE INDEX ix_cache_user_id ON cache(user_id);
+CREATE INDEX ix_cache_cache_key ON cache(cache_key);
+CREATE INDEX ix_cache_session_id ON cache(session_id);
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table contacts
+--
+
+CREATE TABLE contacts (
+ contact_id integer NOT NULL PRIMARY KEY,
+ user_id integer NOT NULL default '0',
+ changed datetime NOT NULL default '0000-00-00 00:00:00',
+ del tinyint NOT NULL default '0',
+ name varchar(128) NOT NULL default '',
+ email varchar(128) NOT NULL default '',
+ firstname varchar(128) NOT NULL default '',
+ surname varchar(128) NOT NULL default '',
+ vcard text NOT NULL default ''
+);
+
+CREATE INDEX ix_contacts_user_id ON contacts(user_id);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table identities
+--
+
+CREATE TABLE identities (
+ identity_id integer NOT NULL PRIMARY KEY,
+ user_id integer NOT NULL default '0',
+ del tinyint NOT NULL default '0',
+ standard tinyint NOT NULL default '0',
+ name varchar(128) NOT NULL default '',
+ organization varchar(128) default '',
+ email varchar(128) NOT NULL default '',
+ "reply-to" varchar(128) NOT NULL default '',
+ bcc varchar(128) NOT NULL default '',
+ signature text NOT NULL default ''
+);
+
+CREATE INDEX ix_identities_user_id ON identities(user_id);
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table users
+--
+
+CREATE TABLE users (
+ user_id integer NOT NULL PRIMARY KEY,
+ username varchar(128) NOT NULL default '',
+ mail_host varchar(128) NOT NULL default '',
+ alias varchar(128) NOT NULL default '',
+ created datetime NOT NULL default '0000-00-00 00:00:00',
+ last_login datetime NOT NULL default '0000-00-00 00:00:00',
+ language varchar(5) NOT NULL default 'en',
+ preferences text NOT NULL default ''
+);
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table session
+--
+
+CREATE TABLE session (
+ sess_id varchar(40) NOT NULL PRIMARY KEY,
+ created datetime NOT NULL default '0000-00-00 00:00:00',
+ changed datetime NOT NULL default '0000-00-00 00:00:00',
+ ip varchar(15) NOT NULL default '',
+ vars text NOT NULL
+);
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table messages
+--
+
+CREATE TABLE messages (
+ message_id integer NOT NULL PRIMARY KEY,
+ user_id integer NOT NULL default '0',
+ del tinyint NOT NULL default '0',
+ cache_key varchar(128) NOT NULL default '',
+ created datetime NOT NULL default '0000-00-00 00:00:00',
+ idx integer NOT NULL default '0',
+ uid integer NOT NULL default '0',
+ subject varchar(255) NOT NULL default '',
+ "from" varchar(255) NOT NULL default '',
+ "to" varchar(255) NOT NULL default '',
+ cc varchar(255) NOT NULL default '',
+ date datetime NOT NULL default '0000-00-00 00:00:00',
+ size integer NOT NULL default '0',
+ headers text NOT NULL,
+ body text
+);
+
+CREATE INDEX ix_messages_user_id ON messages(user_id);
+CREATE INDEX ix_messages_cache_key ON messages(cache_key);
+CREATE INDEX ix_messages_idx ON messages(idx);
+CREATE INDEX ix_messages_uid ON messages(uid);
--- /dev/null
+UPDATE instructions
+===================
+
+Follow these instructions if upgrading from a previous version
+of RoundCube Webmail.
+
+
+from versions 0.1-alpha and 0.1-20050811
+----------------------------------------
+- replace index.php
+- replace all files in folder /program/
+- replace all files in folder /skins/default/
+- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
+- add these line to /config/main.inc.php
+ $rcmail_config['trash_mbox'] = 'Trash';
+ $rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash');
+ $rcmail_config['prefer_html'] = TRUE;
+ $rcmail_config['prettydate'] = TRUE;
+ $rcmail_config['smtp_port'] = 25;
+ $rcmail_config['default_port'] = 143;
+ $rcmail_config['session_lifetime'] = 20;
+ $rcmail_config['skip_deleted'] = FALSE;
+ $rcmail_config['message_sort_col'] = 'date';
+ $rcmail_config['message_sort_order'] = 'DESC';
+ $rcmail_config['log_dir'] = 'logs/';
+ $rcmail_config['temp_dir'] = 'temp/';
+ $rcmail_config['message_cache_lifetime'] = '10d';
+- replace database properties (db_type, db_host, db_user, db_pass, $d_name)
+ in /config/db.inc.php with the following line:
+ $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
+- add these lines to /config/db.inc.php
+ $rcmail_config['db_max_length'] = 512000;
+
+
+from version 0.1-20050820
+----------------------------------------
+- replace index.php
+- replace all files in folder /program/
+- replace all files in folder /skins/default/
+- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
+- add these line to /config/main.inc.php
+ $rcmail_config['prettydate'] = TRUE;
+ $rcmail_config['smtp_port'] = 25;
+ $rcmail_config['default_port'] = 143;
+ $rcmail_config['session_lifetime'] = 20;
+ $rcmail_config['skip_deleted'] = FALSE;
+ $rcmail_config['message_sort_col'] = 'date';
+ $rcmail_config['message_sort_order'] = 'DESC';
+ $rcmail_config['log_dir'] = 'logs/';
+ $rcmail_config['temp_dir'] = 'temp/';
+ $rcmail_config['message_cache_lifetime'] = '10d';
+- replace database properties (db_type, db_host, db_user, db_pass, $d_name)
+ in /config/db.inc.php with the following line:
+ $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
+- add these lines to /config/db.inc.php
+ $rcmail_config['db_max_length'] = 512000;
+
+
+from version 0.1-20051007
+----------------------------------------
+- replace index.php
+- replace all files in folder /program/
+- replace all files in folder /skins/default/
+- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
+- add these lines to /config/main.inc.php
+ $rcmail_config['smtp_auth_type'] = ''; // if you need to specify an auth method for SMTP
+ $rcmail_config['session_lifetime'] = 20; // to specify the session lifetime in minutes
+ $rcmail_config['skip_deleted'] = FALSE;
+ $rcmail_config['message_sort_col'] = 'date';
+ $rcmail_config['message_sort_order'] = 'DESC';
+ $rcmail_config['log_dir'] = 'logs/';
+ $rcmail_config['temp_dir'] = 'temp/';
+ $rcmail_config['message_cache_lifetime'] = '10d';
+ $rcmail_config['drafts_mbox'] = 'Drafts';
+ $rcmail_config['product_name'] = 'RoundCube Webmail';
+ $rcmail_config['read_when_deleted'] = TRUE;
+ $rcmail_config['enable_spellcheck'] = TRUE;
+- add these lines to /config/db.inc.php
+ $rcmail_config['db_max_length'] = 512000;
+ $rcmail_config['db_sequence_user_ids'] = 'user_ids';
+ $rcmail_config['db_sequence_identity_ids'] = 'identity_ids';
+ $rcmail_config['db_sequence_contact_ids'] = 'contact_ids';
+ $rcmail_config['db_sequence_cache_ids'] = 'cache_ids';
+ $rcmail_config['db_sequence_message_ids'] = 'message_ids';
+ $rcmail_config['db_persistent'] = TRUE;
+
+from version 0.1-20051021
+----------------------------------------
+- replace index.php
+- replace all files in folder /program/
+- replace all files in folder /skins/default/
+- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
+- add these lines to /config/main.inc.php
+ $rcmail_config['skip_deleted'] = FALSE;
+ $rcmail_config['message_sort_col'] = 'date';
+ $rcmail_config['message_sort_order'] = 'DESC';
+ $rcmail_config['log_dir'] = 'logs/';
+ $rcmail_config['temp_dir'] = 'temp/';
+ $rcmail_config['message_cache_lifetime'] = '10d';
+ $rcmail_config['drafts_mbox'] = 'Drafts';
+ $rcmail_config['product_name'] = 'RoundCube Webmail';
+ $rcmail_config['read_when_deleted'] = TRUE;
+ $rcmail_config['enable_spellcheck'] = TRUE;
+- add these lines to /config/db.inc.php
+ $rcmail_config['db_max_length'] = 512000;
+ $rcmail_config['db_sequence_user_ids'] = 'user_ids';
+ $rcmail_config['db_sequence_identity_ids'] = 'identity_ids';
+ $rcmail_config['db_sequence_contact_ids'] = 'contact_ids';
+ $rcmail_config['db_sequence_cache_ids'] = 'cache_ids';
+ $rcmail_config['db_sequence_message_ids'] = 'message_ids';
+ $rcmail_config['db_persistent'] = TRUE;
+
+
+form version 0.1-beta
+----------------------------------------
+- replace index.php
+- replace all files in folder /program/
+- replace all files in folder /skins/default/
+- add these line to /config/db.inc.php
+ $rcmail_config['db_persistent'] = TRUE;
+- add these lines to /config/main.inc.php
+ $rcmail_config['drafts_mbox'] = 'Drafts';
+ $rcmail_config['junk_mbox'] = 'Junk';
+ $rcmail_config['product_name'] = 'RoundCube Webmail';
+ $rcmail_config['read_when_deleted'] = TRUE;
+ $rcmail_config['enable_spellcheck'] = TRUE;
+ $rcmail_config['protect_default_folders'] = TRUE;
+- replace the following line from /config/main.inc.php
+ @include($_SERVER['HTTP_HOST'].'.inc.php');
+ with
+ $rcmail_config['include_host_config'] = TRUE;
+
+
--- /dev/null
+Order allow,deny
+Deny from all
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | Configuration file for database access |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+
+*/
+
+$rcmail_config = array();
+
+// PEAR database DSN for read/write operations
+// format is db_provider://user:password@host/databse
+// currentyl suported db_providers: mysql, sqlite
+
+$rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
+// postgres example: 'pgsql://roundcube:pass@localhost/roundcubemail';
+// sqlite example: 'sqlite://./sqlite.db?mode=0646';
+
+// PEAR database DSN for read only operations (if empty write database will be used)
+// useful for database replication
+$rcmail_config['db_dsnr'] = '';
+
+// database backend to use (only db or mdb2 are supported)
+$rcmail_config['db_backend'] = 'db';
+
+// maximum length of a query in bytes
+$rcmail_config['db_max_length'] = 512000; // 500K
+
+// use persistent db-connections
+$rcmail_config['db_persistent'] = TRUE;
+
+
+// you can define specific table names used to store webmail data
+$rcmail_config['db_table_users'] = 'users';
+
+$rcmail_config['db_table_identities'] = 'identities';
+
+$rcmail_config['db_table_contacts'] = 'contacts';
+
+$rcmail_config['db_table_session'] = 'session';
+
+$rcmail_config['db_table_cache'] = 'cache';
+
+$rcmail_config['db_table_messages'] = 'messages';
+
+
+// you can define specific sequence names used in PostgreSQL
+$rcmail_config['db_sequence_users'] = 'user_ids';
+
+$rcmail_config['db_sequence_identities'] = 'identity_ids';
+
+$rcmail_config['db_sequence_contacts'] = 'contact_ids';
+
+$rcmail_config['db_sequence_cache'] = 'cache_ids';
+
+$rcmail_config['db_sequence_messages'] = 'message_ids';
+
+
+// end db config file
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | Main configuration file |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+
+*/
+
+$rcmail_config = array();
+
+
+// system error reporting: 1 = log; 2 = report (not implemented yet), 4 = show, 8 = trace
+$rcmail_config['debug_level'] = 1;
+
+// enable caching of messages and mailbox data in the local database.
+// this is recommended if the IMAP server does not run on the same machine
+$rcmail_config['enable_caching'] = TRUE;
+
+// lifetime of message cache
+// possible units: s, m, h, d, w
+$rcmail_config['message_cache_lifetime'] = '10d';
+
+// automatically create a new RoundCube user when log-in the first time.
+// a new user will be created once the IMAP login succeeds.
+// set to false if only registered users can use this service
+$rcmail_config['auto_create_user'] = TRUE;
+
+// the mail host chosen to perform the log-in
+// leave blank to show a textbox at login, give a list of hosts
+// to display a pulldown menu or set one host as string.
+// To use SSL connection, enter ssl://hostname:993
+$rcmail_config['default_host'] = '';
+
+// TCP port used for IMAP connections
+$rcmail_config['default_port'] = 143;
+
+// Automatically add this domain to user names for login
+// Only for IMAP servers that require full e-mail addresses for login
+// Specify an array with 'host' => 'domain' values to support multiple hosts
+$rcmail_config['username_domain'] = '';
+
+// This domain will be used to form e-mail addresses of new users
+// Specify an array with 'host' => 'domain' values to support multiple hosts
+$rcmail_config['mail_domain'] = '';
+
+// Path to a virtuser table file to resolve user names and e-mail addresses
+$rcmail_config['virtuser_file'] = '';
+
+// Query to resolve user names and e-mail addresses from the database
+// %u will be replaced with the current username for login.
+// The query should select the user's e-mail address as first col
+$rcmail_config['virtuser_query'] = '';
+
+// use this host for sending mails.
+// to use SSL connection, set ssl://smtp.host.com
+// if left blank, the PHP mail() function is used
+$rcmail_config['smtp_server'] = '';
+
+// SMTP port (default is 25; 465 for SSL)
+$rcmail_config['smtp_port'] = 25;
+
+// SMTP username (if required) if you use %u as the username RoundCube
+// will use the current username for login
+$rcmail_config['smtp_user'] = '';
+
+// SMTP password (if required) if you use %p as the password RoundCube
+// will use the current user's password for login
+$rcmail_config['smtp_pass'] = '';
+
+// SMTP AUTH type (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or empty to use
+// best server supported one)
+$rcmail_config['smtp_auth_type'] = '';
+
+// Log sent messages
+$rcmail_config['smtp_log'] = TRUE;
+
+// these cols are shown in the message list
+// available cols are: subject, from, to, cc, replyto, date, size, encoding
+$rcmail_config['list_cols'] = array('subject', 'from', 'date', 'size');
+
+// relative path to the skin folder
+$rcmail_config['skin_path'] = 'skins/default/';
+
+// use this folder to store temp files (must be writebale for apache user)
+$rcmail_config['temp_dir'] = 'temp/';
+
+// use this folder to store log files (must be writebale for apache user)
+$rcmail_config['log_dir'] = 'logs/';
+
+// session lifetime in minutes
+$rcmail_config['session_lifetime'] = 10;
+
+// check client IP in session athorization
+$rcmail_config['ip_check'] = TRUE;
+
+// this key is used to encrypt the users imap password which is stored
+// in the session record (and the client cookie if remember password is enabled).
+// please provide a string of exactly 24 chars.
+$rcmail_config['des_key'] = 'rcmail-!24ByteDESkey*Str';
+
+// the default locale setting
+$rcmail_config['locale_string'] = 'en';
+
+// use this format for short date display
+$rcmail_config['date_short'] = 'D H:i';
+
+// use this format for detailed date/time formatting
+$rcmail_config['date_long'] = 'd.m.Y H:i';
+
+// add this user-agent to message headers when sending
+$rcmail_config['useragent'] = 'RoundCube Webmail/0.1b';
+
+// use this name to compose page titles
+$rcmail_config['product_name'] = 'RoundCube Webmail';
+
+// only list folders within this path
+$rcmail_config['imap_root'] = '';
+
+// store draft message is this mailbox
+// leave blank if draft messages should not be stored
+$rcmail_config['drafts_mbox'] = 'Drafts';
+
+// store spam messages in this mailbox
+$rcmail_config['junk_mbox'] = 'Junk';
+
+// store sent message is this mailbox
+// leave blank if sent messages should not be stored
+$rcmail_config['sent_mbox'] = 'Sent';
+
+// move messages to this folder when deleting them
+// leave blank if they should be deleted directly
+$rcmail_config['trash_mbox'] = 'Trash';
+
+// display these folders separately in the mailbox list.
+// these folders will automatically be created if they do not exist
+$rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash');
+
+// protect the default folders from renames, deletes, and subscription changes
+$rcmail_config['protect_default_folders'] = TRUE;
+
+// Set TRUE if deleted messages should not be displayed
+// This will make the application run slower
+$rcmail_config['skip_deleted'] = FALSE;
+
+// Set true to Mark deleted messages as read as well as deleted
+// False means that a message's read status is not affected by marking it as deleted
+$rcmail_config['read_when_deleted'] = TRUE;
+
+// When a Trash folder is not present and a message is deleted, flag
+// the message for deletion rather than deleting it immediately. Setting this to
+// false causes deleted messages to be permanantly removed if there is no Trash folder
+$rcmail_config['flag_for_deletion'] = TRUE;
+
+// Make use of the built-in spell checker. It is based on GoogieSpell
+// which means that the message content will be sent to Google in order to check spelling
+$rcmail_config['enable_spellcheck'] = TRUE;
+
+// path to a text file which will be added to each sent message
+// paths are relative to the RoundCube root folder
+$rcmail_config['generic_message_footer'] = '';
+
+// this string is used as a delimiter for message headers when sending
+// leave empty for auto-detection
+$rcmail_config['mail_header_delimiter'] = NULL;
+
+// in order to enable public ldap search, create a config array
+// like the Verisign example below. if you would like to test,
+// simply uncomment the Verisign example.
+/**
+ * example config for Verisign directory
+ *
+ * $rcmail_config['ldap_public']['Verisign'] = array('hosts' => array('directory.verisign.com'),
+ * 'port' => 389,
+ * 'base_dn' => '',
+ * 'search_fields' => array('Email' => 'mail', 'Name' => 'cn'),
+ * 'name_field' => 'cn',
+ * 'mail_field' => 'mail',
+ * 'scope' => 'sub',
+ * 'fuzzy_search' => 0);
+ */
+
+// try to load host-specific configuration
+$rcmail_config['include_host_config'] = FALSE;
+
+
+/***** these settings can be overwritten by user's preferences *****/
+
+// show up to X items in list view
+$rcmail_config['pagesize'] = 40;
+
+// use this timezone to display date/time
+$rcmail_config['timezone'] = 1;
+
+// daylight savings are On
+$rcmail_config['dst_active'] = TRUE;
+
+// prefer displaying HTML messages
+$rcmail_config['prefer_html'] = TRUE;
+
+// show pretty dates as standard
+$rcmail_config['prettydate'] = TRUE;
+
+// default sort col
+$rcmail_config['message_sort_col'] = 'date';
+
+// default sort order
+$rcmail_config['message_sort_order'] = 'DESC';
+
+// list of configuration option names that need to be available in Javascript.
+$rcmail_config['javascript_config'] = array('read_when_deleted', 'flag_for_deletion');
+
+
+// end of config file
+?>
--- /dev/null
+<?php
+/*
+ +-----------------------------------------------------------------------+
+ | RoundCube Webmail IMAP Client |
+ | Version 0.1-beta2 |
+ | |
+ | Copyright (C) 2005-2006, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | Redistribution and use in source and binary forms, with or without |
+ | modification, are permitted provided that the following conditions |
+ | are met: |
+ | |
+ | o Redistributions of source code must retain the above copyright |
+ | notice, this list of conditions and the following disclaimer. |
+ | o Redistributions in binary form must reproduce the above copyright |
+ | notice, this list of conditions and the following disclaimer in the |
+ | documentation and/or other materials provided with the distribution.|
+ | o The names of the authors may not be used to endorse or promote |
+ | products derived from this software without specific prior written |
+ | permission. |
+ | |
+ | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: index.php 429 2006-12-22 22:26:24Z thomasb $
+
+*/
+
+define('RCMAIL_VERSION', '0.1-beta2');
+
+// define global vars
+$CHARSET = 'UTF-8';
+$OUTPUT_TYPE = 'html';
+$JS_OBJECT_NAME = 'rcmail';
+$INSTALL_PATH = dirname(__FILE__);
+$MAIN_TASKS = array('mail','settings','addressbook','logout');
+
+if (empty($INSTALL_PATH))
+ $INSTALL_PATH = './';
+else
+ $INSTALL_PATH .= '/';
+
+
+// make sure path_separator is defined
+if (!defined('PATH_SEPARATOR'))
+ define('PATH_SEPARATOR', (eregi('win', PHP_OS) ? ';' : ':'));
+
+
+// RC include folders MUST be included FIRST to avoid other
+// possible not compatible libraries (i.e PEAR) to be included
+// instead the ones provided by RC
+ini_set('include_path', $INSTALL_PATH.PATH_SEPARATOR.$INSTALL_PATH.'program'.PATH_SEPARATOR.$INSTALL_PATH.'program/lib'.PATH_SEPARATOR.ini_get('include_path'));
+
+ini_set('session.name', 'sessid');
+ini_set('session.use_cookies', 1);
+ini_set('session.gc_maxlifetime', 21600);
+ini_set('session.gc_divisor', 500);
+ini_set('error_reporting', E_ALL&~E_NOTICE);
+
+// increase maximum execution time for php scripts
+// (does not work in safe mode)
+@set_time_limit(120);
+
+// include base files
+require_once('include/rcube_shared.inc');
+require_once('include/rcube_imap.inc');
+require_once('include/bugs.inc');
+require_once('include/main.inc');
+require_once('include/cache.inc');
+require_once('PEAR.php');
+
+
+// set PEAR error handling
+// PEAR::setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_NOTICE);
+
+// use gzip compression if supported
+if (function_exists('ob_gzhandler') && !ini_get('zlib.output_compression'))
+ ob_start('ob_gzhandler');
+else
+ ob_start();
+
+
+// catch some url/post parameters
+$_task = strip_quotes(get_input_value('_task', RCUBE_INPUT_GPC));
+$_action = strip_quotes(get_input_value('_action', RCUBE_INPUT_GPC));
+$_framed = (!empty($_GET['_framed']) || !empty($_POST['_framed']));
+
+// use main task if empty or invalid value
+if (empty($_task) || !in_array($_task, $MAIN_TASKS))
+ $_task = 'mail';
+
+if (!empty($_GET['_remote']))
+ $REMOTE_REQUEST = TRUE;
+
+// start session with requested task
+rcmail_startup($_task);
+
+// set session related variables
+$COMM_PATH = sprintf('./?_task=%s', $_task);
+$SESS_HIDDEN_FIELD = '';
+
+
+// add framed parameter
+if ($_framed)
+ {
+ $COMM_PATH .= '&_framed=1';
+ $SESS_HIDDEN_FIELD .= "\n".'<input type="hidden" name="_framed" value="1" />';
+ }
+
+
+// init necessary objects for GUI
+load_gui();
+
+
+// check DB connections and exit on failure
+if ($err_str = $DB->is_error())
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $err_str), FALSE, TRUE);
+ }
+
+
+// error steps
+if ($_action=='error' && !empty($_GET['_code']))
+ {
+ raise_error(array('code' => hexdec($_GET['_code'])), FALSE, TRUE);
+ }
+
+
+// try to log in
+if ($_action=='login' && $_task=='mail')
+ {
+ $host = $_POST['_host'] ? $_POST['_host'] : $CONFIG['default_host'];
+
+ // check if client supports cookies
+ if (empty($_COOKIE))
+ {
+ show_message("cookiesdisabled", 'warning');
+ }
+ else if (isset($_POST['_user']) && isset($_POST['_pass']) &&
+ rcmail_login(get_input_value('_user', RCUBE_INPUT_POST), $_POST['_pass'], $host))
+ {
+ // send redirect
+ header("Location: $COMM_PATH");
+ exit;
+ }
+ else
+ {
+ show_message("loginfailed", 'warning');
+ $_SESSION['user_id'] = '';
+ }
+ }
+
+// end session
+else if ($_action=='logout' && isset($_SESSION['user_id']))
+ {
+ show_message('loggedout');
+ rcmail_kill_session();
+ }
+
+// check session and auth cookie
+else if ($_action!='login' && $_SESSION['user_id'])
+ {
+ if (!rcmail_authenticate_session() ||
+ ($CONFIG['session_lifetime'] && isset($SESS_CHANGED) && $SESS_CHANGED + $CONFIG['session_lifetime']*60 < mktime()))
+ {
+ $message = show_message('sessionerror', 'error');
+ rcmail_kill_session();
+ }
+ }
+
+
+// log in to imap server
+if (!empty($_SESSION['user_id']) && $_task=='mail')
+ {
+ $conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']);
+ if (!$conn)
+ {
+ show_message('imaperror', 'error');
+ $_SESSION['user_id'] = '';
+ }
+ else
+ rcmail_set_imap_prop();
+ }
+
+
+// not logged in -> set task to 'login
+if (empty($_SESSION['user_id']))
+ {
+ if ($REMOTE_REQUEST)
+ {
+ $message .= "setTimeout(\"location.href='\"+this.env.comm_path+\"'\", 2000);";
+ rcube_remote_response($message);
+ }
+
+ $_task = 'login';
+ }
+
+
+
+// set task and action to client
+$script = sprintf("%s.set_env('task', '%s');", $JS_OBJECT_NAME, $_task);
+if (!empty($_action))
+ $script .= sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action);
+
+$OUTPUT->add_script($script);
+
+
+
+// not logged in -> show login page
+if (!$_SESSION['user_id'])
+ {
+ parse_template('login');
+ exit;
+ }
+
+
+// handle keep-alive signal
+if ($_action=='keep-alive')
+ {
+ rcube_remote_response('');
+ exit;
+ }
+
+
+// include task specific files
+if ($_task=='mail')
+ {
+ include_once('program/steps/mail/func.inc');
+
+ if ($_action=='show' || $_action=='print')
+ include('program/steps/mail/show.inc');
+
+ if ($_action=='get')
+ include('program/steps/mail/get.inc');
+
+ if ($_action=='moveto' || $_action=='delete')
+ include('program/steps/mail/move_del.inc');
+
+ if ($_action=='mark')
+ include('program/steps/mail/mark.inc');
+
+ if ($_action=='viewsource')
+ include('program/steps/mail/viewsource.inc');
+
+ if ($_action=='send')
+ include('program/steps/mail/sendmail.inc');
+
+ if ($_action=='upload')
+ include('program/steps/mail/upload.inc');
+
+ if ($_action=='compose' || $_action=='remove-attachment')
+ include('program/steps/mail/compose.inc');
+
+ if ($_action=='addcontact')
+ include('program/steps/mail/addcontact.inc');
+
+ if ($_action=='expunge' || $_action=='purge')
+ include('program/steps/mail/folders.inc');
+
+ if ($_action=='check-recent')
+ include('program/steps/mail/check_recent.inc');
+
+ if ($_action=='getunread')
+ include('program/steps/mail/getunread.inc');
+
+ if ($_action=='list' && isset($_GET['_remote']))
+ include('program/steps/mail/list.inc');
+
+ if ($_action=='search')
+ include('program/steps/mail/search.inc');
+
+ if ($_action=='spell')
+ include('program/steps/mail/spell.inc');
+
+ if ($_action=='rss')
+ include('program/steps/mail/rss.inc');
+
+
+ // make sure the message count is refreshed
+ $IMAP->messagecount($_SESSION['mbox'], 'ALL', TRUE);
+ }
+
+
+// include task specific files
+if ($_task=='addressbook')
+ {
+ include_once('program/steps/addressbook/func.inc');
+
+ if ($_action=='save')
+ include('program/steps/addressbook/save.inc');
+
+ if ($_action=='edit' || $_action=='add')
+ include('program/steps/addressbook/edit.inc');
+
+ if ($_action=='delete')
+ include('program/steps/addressbook/delete.inc');
+
+ if ($_action=='show')
+ include('program/steps/addressbook/show.inc');
+
+ if ($_action=='list' && $_GET['_remote'])
+ include('program/steps/addressbook/list.inc');
+
+ if ($_action=='ldappublicsearch')
+ include('program/steps/addressbook/ldapsearchform.inc');
+ }
+
+
+// include task specific files
+if ($_task=='settings')
+ {
+ include_once('program/steps/settings/func.inc');
+
+ if ($_action=='save-identity')
+ include('program/steps/settings/save_identity.inc');
+
+ if ($_action=='add-identity' || $_action=='edit-identity')
+ include('program/steps/settings/edit_identity.inc');
+
+ if ($_action=='delete-identity')
+ include('program/steps/settings/delete_identity.inc');
+
+ if ($_action=='identities')
+ include('program/steps/settings/identities.inc');
+
+ if ($_action=='save-prefs')
+ include('program/steps/settings/save_prefs.inc');
+
+ if ($_action=='folders' || $_action=='subscribe' || $_action=='unsubscribe' ||
+ $_action=='create-folder' || $_action=='rename-folder' || $_action=='delete-folder')
+ include('program/steps/settings/manage_folders.inc');
+
+ }
+
+
+// parse main template
+parse_template($_task);
+
+
+// if we arrive here, something went wrong
+raise_error(array('code' => 404,
+ 'type' => 'php',
+ 'line' => __LINE__,
+ 'file' => __FILE__,
+ 'message' => "Invalid request"), TRUE, TRUE);
+
+?>
--- /dev/null
+Order allow,deny
+Deny from all
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/bugs.inc |
+ | |
+ | This file is part of the BQube Webmail client |
+ | Copyright (C) 2005, BQube Dev - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide error handling and logging functions |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: bugs.inc 90 2005-12-11 23:19:48Z roundcube $
+
+*/
+
+
+// throw system error and show error page
+function raise_error($arg=array(), $log=FALSE, $terminate=FALSE)
+ {
+ global $__page_content, $CONFIG, $OUTPUT, $ERROR_CODE, $ERROR_MESSAGE;
+
+ /* $arg keys:
+ int code
+ string type (php, xpath, db, imap, javascript)
+ string message
+ sring file
+ int line
+ */
+
+ // report bug (if not incompatible browser)
+ if ($log && $arg['type'] && $arg['message'])
+ log_bug($arg);
+
+ // display error page and terminate script
+ if ($terminate)
+ {
+ $ERROR_CODE = $arg['code'];
+ $ERROR_MESSAGE = $arg['message'];
+ include("program/steps/error.inc");
+ exit;
+ }
+ }
+
+
+// report error
+function log_bug($arg_arr)
+ {
+ global $CONFIG, $INSTALL_PATH;
+ $program = $arg_arr['type']=='xpath' ? 'XPath' : strtoupper($arg_arr['type']);
+
+ // write error to local log file
+ if ($CONFIG['debug_level'] & 1)
+ {
+ $log_entry = sprintf("[%s] %s Error: %s in %s on line %d\n",
+ date("d-M-Y H:i:s O", mktime()),
+ $program,
+ $arg_arr['message'],
+ $arg_arr['file'],
+ $arg_arr['line']);
+
+ if (empty($CONFIG['log_dir']))
+ $CONFIG['log_dir'] = $INSTALL_PATH.'logs';
+
+ // try to open specific log file for writing
+ if ($fp = @fopen($CONFIG['log_dir'].'/errors', 'a'))
+
+ {
+ fwrite($fp, $log_entry);
+ fclose($fp);
+ }
+ else
+ {
+ // send error to PHPs error handler
+ trigger_error($arg_arr['message']);
+ }
+ }
+
+/*
+ // resport the bug to the global bug reporting system
+ if ($CONFIG['debug_level'] & 2)
+ {
+ $delm = '%AC';
+ http_request(sprintf('http://roundcube.net/log/bug.php?_type=%s&_domain=%s&_server_ip=%s&_client_ip=%s&_useragent=%s&_url=%s%%3A//%s&_errors=%s%s%s%s%s',
+ $arg_arr['type'],
+ $GLOBALS['HTTP_HOST'],
+ $GLOBALS['SERVER_ADDR'],
+ $GLOBALS['REMOTE_ADDR'],
+ rawurlencode($GLOBALS['HTTP_USER_AGENT']),
+ $GLOBALS['SERVER_PORT']==43 ? 'https' : 'http',
+ $GLOBALS['HTTP_HOST'].$GLOBALS['REQUEST_URI'],
+ $arg_arr['file'], $delm,
+ $arg_arr['line'], $delm,
+ rawurlencode($arg_arr['message'])));
+ }
+*/
+
+ // show error if debug_mode is on
+ if ($CONFIG['debug_level'] & 4)
+ {
+ print "<b>$program Error";
+
+ if (!empty($arg_arr['file']) && !empty($arg_arr['line']))
+ print " in $arg_arr[file] ($arg_arr[line])";
+
+ print ":</b> ";
+ print nl2br($arg_arr['message']);
+ print '<br />';
+ flush();
+ }
+ }
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/cache.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev, - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide access to the application cache |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: cache.inc 88 2005-12-03 16:54:12Z roundcube $
+
+*/
+
+
+function rcube_read_cache($key)
+ {
+ global $DB, $CACHE_KEYS;
+
+ // query db
+ $sql_result = $DB->query("SELECT cache_id, data
+ FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $key);
+
+ // get cached data
+ if ($sql_arr = $DB->fetch_assoc($sql_result))
+ {
+ $data = $sql_arr['data'];
+ $CACHE_KEYS[$key] = $sql_arr['cache_id'];
+ }
+ else
+ $data = FALSE;
+
+ return $data;
+ }
+
+
+function rcube_write_cache($key, $data, $session_cache=FALSE)
+ {
+ global $DB, $CACHE_KEYS, $sess_id;
+
+ // check if we already have a cache entry for this key
+ if (!isset($CACHE_KEYS[$key]))
+ {
+ $sql_result = $DB->query("SELECT cache_id
+ FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $key);
+
+ if ($sql_arr = $DB->fetch_assoc($sql_result))
+ $CACHE_KEYS[$key] = $sql_arr['cache_id'];
+ else
+ $CACHE_KEYS[$key] = FALSE;
+ }
+
+ // update existing cache record
+ if ($CACHE_KEYS[$key])
+ {
+ $DB->query("UPDATE ".get_table_name('cache')."
+ SET created=now(),
+ data=?
+ WHERE user_id=?
+ AND cache_key=?",
+ $data,
+ $_SESSION['user_id'],
+ $key);
+ }
+ // add new cache record
+ else
+ {
+ $DB->query("INSERT INTO ".get_table_name('cache')."
+ (created, user_id, session_id, cache_key, data)
+ VALUES (now(), ?, ?, ?, ?)",
+ $_SESSION['user_id'],
+ $session_cache ? $sess_id : 'NULL',
+ $key,
+ $data);
+ }
+ }
+
+
+function rcube_clear_cache($key)
+ {
+ global $DB;
+
+ $DB->query("DELETE FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $key);
+ }
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/main.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev, - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide basic functions for the webmail package |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: main.inc 429 2006-12-22 22:26:24Z thomasb $
+
+*/
+
+require_once('lib/des.inc');
+require_once('lib/utf7.inc');
+require_once('lib/utf8.class.php');
+
+
+// define constannts for input reading
+define('RCUBE_INPUT_GET', 0x0101);
+define('RCUBE_INPUT_POST', 0x0102);
+define('RCUBE_INPUT_GPC', 0x0103);
+
+
+// register session and connect to server
+function rcmail_startup($task='mail')
+ {
+ global $sess_id, $sess_auth, $sess_user_lang;
+ global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB, $JS_OBJECT_NAME;
+
+ // check client
+ $BROWSER = rcube_browser();
+
+ // load config file
+ include_once('config/main.inc.php');
+ $CONFIG = is_array($rcmail_config) ? $rcmail_config : array();
+
+ // load host-specific configuration
+ rcmail_load_host_config($CONFIG);
+
+ $CONFIG['skin_path'] = $CONFIG['skin_path'] ? unslashify($CONFIG['skin_path']) : 'skins/default';
+
+ // load db conf
+ include_once('config/db.inc.php');
+ $CONFIG = array_merge($CONFIG, $rcmail_config);
+
+ if (empty($CONFIG['log_dir']))
+ $CONFIG['log_dir'] = $INSTALL_PATH.'logs';
+ else
+ $CONFIG['log_dir'] = unslashify($CONFIG['log_dir']);
+
+ // set PHP error logging according to config
+ if ($CONFIG['debug_level'] & 1)
+ {
+ ini_set('log_errors', 1);
+ ini_set('error_log', $CONFIG['log_dir'].'/errors');
+ }
+ if ($CONFIG['debug_level'] & 4)
+ ini_set('display_errors', 1);
+ else
+ ini_set('display_errors', 0);
+
+
+ // set session garbage collecting time according to session_lifetime
+ if (!empty($CONFIG['session_lifetime']))
+ ini_set('session.gc_maxlifetime', ($CONFIG['session_lifetime']+2)*60);
+
+
+ // prepare DB connection
+ require_once('include/rcube_'.(empty($CONFIG['db_backend']) ? 'db' : $CONFIG['db_backend']).'.inc');
+
+ $DB = new rcube_db($CONFIG['db_dsnw'], $CONFIG['db_dsnr'], $CONFIG['db_persistent']);
+ $DB->sqlite_initials = $INSTALL_PATH.'SQL/sqlite.initial.sql';
+ $DB->db_connect('w');
+
+ // we can use the database for storing session data
+ if (!$DB->is_error())
+ include_once('include/session.inc');
+
+ // init session
+ session_start();
+ $sess_id = session_id();
+
+ // create session and set session vars
+ if (!isset($_SESSION['auth_time']))
+ {
+ $_SESSION['user_lang'] = rcube_language_prop($CONFIG['locale_string']);
+ $_SESSION['auth_time'] = mktime();
+ setcookie('sessauth', rcmail_auth_hash($sess_id, $_SESSION['auth_time']));
+ }
+
+ // set session vars global
+ $sess_user_lang = rcube_language_prop($_SESSION['user_lang']);
+
+
+ // overwrite config with user preferences
+ if (is_array($_SESSION['user_prefs']))
+ $CONFIG = array_merge($CONFIG, $_SESSION['user_prefs']);
+
+
+ // reset some session parameters when changing task
+ if ($_SESSION['task'] != $task)
+ unset($_SESSION['page']);
+
+ // set current task to session
+ $_SESSION['task'] = $task;
+
+ // create IMAP object
+ if ($task=='mail')
+ rcmail_imap_init();
+
+
+ // set localization
+ if ($CONFIG['locale_string'])
+ setlocale(LC_ALL, $CONFIG['locale_string']);
+ else if ($sess_user_lang)
+ setlocale(LC_ALL, $sess_user_lang);
+
+
+ register_shutdown_function('rcmail_shutdown');
+ }
+
+
+// load a host-specific config file if configured
+function rcmail_load_host_config(&$config)
+ {
+ $fname = NULL;
+
+ if (is_array($config['include_host_config']))
+ $fname = $config['include_host_config'][$_SERVER['HTTP_HOST']];
+ else if (!empty($config['include_host_config']))
+ $fname = preg_replace('/[^a-z0-9\.\-_]/i', '', $_SERVER['HTTP_HOST']) . '.inc.php';
+
+ if ($fname && is_file('config/'.$fname))
+ {
+ include('config/'.$fname);
+ $config = array_merge($config, $rcmail_config);
+ }
+ }
+
+
+// create authorization hash
+function rcmail_auth_hash($sess_id, $ts)
+ {
+ global $CONFIG;
+
+ $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
+ $sess_id,
+ $ts,
+ $CONFIG['ip_check'] ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
+ $_SERVER['HTTP_USER_AGENT']);
+
+ if (function_exists('sha1'))
+ return sha1($auth_string);
+ else
+ return md5($auth_string);
+ }
+
+
+// compare the auth hash sent by the client with the local session credentials
+function rcmail_authenticate_session()
+ {
+ $now = mktime();
+ $valid = ($_COOKIE['sessauth'] == rcmail_auth_hash(session_id(), $_SESSION['auth_time']));
+
+ // renew auth cookie every 5 minutes (only for GET requests)
+ if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now-$_SESSION['auth_time'] > 300))
+ {
+ $_SESSION['auth_time'] = $now;
+ setcookie('sessauth', rcmail_auth_hash(session_id(), $now));
+ }
+
+ return $valid;
+ }
+
+
+// create IMAP object and connect to server
+function rcmail_imap_init($connect=FALSE)
+ {
+ global $CONFIG, $DB, $IMAP;
+
+ $IMAP = new rcube_imap($DB);
+ $IMAP->debug_level = $CONFIG['debug_level'];
+ $IMAP->skip_deleted = $CONFIG['skip_deleted'];
+
+
+ // connect with stored session data
+ if ($connect)
+ {
+ if (!($conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl'])))
+ show_message('imaperror', 'error');
+
+ rcmail_set_imap_prop();
+ }
+
+ // enable caching of imap data
+ if ($CONFIG['enable_caching']===TRUE)
+ $IMAP->set_caching(TRUE);
+
+ // set pagesize from config
+ if (isset($CONFIG['pagesize']))
+ $IMAP->set_pagesize($CONFIG['pagesize']);
+ }
+
+
+// set root dir and last stored mailbox
+// this must be done AFTER connecting to the server
+function rcmail_set_imap_prop()
+ {
+ global $CONFIG, $IMAP;
+
+ // set root dir from config
+ if (!empty($CONFIG['imap_root']))
+ $IMAP->set_rootdir($CONFIG['imap_root']);
+
+ if (is_array($CONFIG['default_imap_folders']))
+ $IMAP->set_default_mailboxes($CONFIG['default_imap_folders']);
+
+ if (!empty($_SESSION['mbox']))
+ $IMAP->set_mailbox($_SESSION['mbox']);
+ if (isset($_SESSION['page']))
+ $IMAP->set_page($_SESSION['page']);
+ }
+
+
+// do these things on script shutdown
+function rcmail_shutdown()
+ {
+ global $IMAP;
+
+ if (is_object($IMAP))
+ {
+ $IMAP->close();
+ $IMAP->write_cache();
+ }
+
+ // before closing the database connection, write session data
+ session_write_close();
+ }
+
+
+// destroy session data and remove cookie
+function rcmail_kill_session()
+ {
+ // save user preferences
+ $a_user_prefs = $_SESSION['user_prefs'];
+ if (!is_array($a_user_prefs))
+ $a_user_prefs = array();
+
+ if ((isset($_SESSION['sort_col']) && $_SESSION['sort_col']!=$a_user_prefs['message_sort_col']) ||
+ (isset($_SESSION['sort_order']) && $_SESSION['sort_order']!=$a_user_prefs['message_sort_order']))
+ {
+ $a_user_prefs['message_sort_col'] = $_SESSION['sort_col'];
+ $a_user_prefs['message_sort_order'] = $_SESSION['sort_order'];
+ rcmail_save_user_prefs($a_user_prefs);
+ }
+
+ $_SESSION = array();
+ session_destroy();
+ }
+
+
+// return correct name for a specific database table
+function get_table_name($table)
+ {
+ global $CONFIG;
+
+ // return table name if configured
+ $config_key = 'db_table_'.$table;
+
+ if (strlen($CONFIG[$config_key]))
+ return $CONFIG[$config_key];
+
+ return $table;
+ }
+
+
+// return correct name for a specific database sequence
+// (used for Postres only)
+function get_sequence_name($sequence)
+ {
+ global $CONFIG;
+
+ // return table name if configured
+ $config_key = 'db_sequence_'.$sequence;
+
+ if (strlen($CONFIG[$config_key]))
+ return $CONFIG[$config_key];
+
+ return $table;
+ }
+
+
+// check the given string and returns language properties
+function rcube_language_prop($lang, $prop='lang')
+ {
+ global $INSTALL_PATH;
+ static $rcube_languages, $rcube_language_aliases, $rcube_charsets;
+
+ 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
+ if (!isset($rcube_languages[$lang]) && strlen($lang)>2)
+ {
+ $lang = substr($lang, 0, 2);
+ $lang = rcube_language_prop($lang);
+ }
+
+ if (!isset($rcube_languages[$lang]))
+ $lang = 'en_US';
+
+ // language has special charset configured
+ if (isset($rcube_charsets[$lang]))
+ $charset = $rcube_charsets[$lang];
+ else
+ $charset = 'UTF-8';
+
+
+ if ($prop=='charset')
+ return $charset;
+ else
+ return $lang;
+ }
+
+
+// init output object for GUI and add common scripts
+function load_gui()
+ {
+ global $CONFIG, $OUTPUT, $COMM_PATH, $JS_OBJECT_NAME, $sess_user_lang;
+
+ // init output page
+ $OUTPUT = new rcube_html_page();
+
+ // add common javascripts
+ $javascript = "var $JS_OBJECT_NAME = new rcube_webmail();\n";
+ $javascript .= "$JS_OBJECT_NAME.set_env('comm_path', '$COMM_PATH');\n";
+
+ if (isset($CONFIG['javascript_config'] )){
+ foreach ($CONFIG['javascript_config'] as $js_config_var){
+ $javascript .= "$JS_OBJECT_NAME.set_env('$js_config_var', '" . $CONFIG[$js_config_var] . "');\n";
+ }
+ }
+
+ if (!empty($GLOBALS['_framed']))
+ $javascript .= "$JS_OBJECT_NAME.set_env('framed', true);\n";
+
+ $OUTPUT->add_script($javascript);
+ $OUTPUT->include_script('common.js');
+ $OUTPUT->include_script('app.js');
+ $OUTPUT->scripts_path = 'program/js/';
+
+ // set locale setting
+ rcmail_set_locale($sess_user_lang);
+
+ // set user-selected charset
+ if (!empty($CONFIG['charset']))
+ $OUTPUT->set_charset($CONFIG['charset']);
+
+ // add some basic label to client
+ rcube_add_label('loading','checkingmail');
+ }
+
+
+// set localization charset based on the given language
+function rcmail_set_locale($lang)
+ {
+ global $OUTPUT, $MBSTRING, $MBSTRING_ENCODING;
+ static $s_mbstring_loaded = NULL;
+
+ // settings for mbstring module (by Tadashi Jokagi)
+ if ($s_mbstring_loaded===NULL)
+ {
+ if ($s_mbstring_loaded = extension_loaded("mbstring"))
+ {
+ $MBSTRING = TRUE;
+ if (function_exists("mb_mbstring_encodings"))
+ $MBSTRING_ENCODING = mb_mbstring_encodings();
+ else
+ $MBSTRING_ENCODING = array("ISO-8859-1", "UTF-7", "UTF7-IMAP", "UTF-8",
+ "ISO-2022-JP", "EUC-JP", "EUCJP-WIN",
+ "SJIS", "SJIS-WIN");
+
+ $MBSTRING_ENCODING = array_map("strtoupper", $MBSTRING_ENCODING);
+ if (in_array("SJIS", $MBSTRING_ENCODING))
+ $MBSTRING_ENCODING[] = "SHIFT_JIS";
+ }
+ else
+ {
+ $MBSTRING = FALSE;
+ $MBSTRING_ENCODING = array();
+ }
+ }
+
+ if ($MBSTRING && function_exists("mb_language"))
+ {
+ if (!@mb_language(strtok($lang, "_")))
+ $MBSTRING = FALSE; // unsupport language
+ }
+
+ $OUTPUT->set_charset(rcube_language_prop($lang, 'charset'));
+ }
+
+
+// perfom login to the IMAP server and to the webmail service
+function rcmail_login($user, $pass, $host=NULL)
+ {
+ global $CONFIG, $IMAP, $DB, $sess_user_lang;
+ $user_id = NULL;
+
+ 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 != $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'))) ? TRUE : FALSE;
+ $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : $CONFIG['default_port']);
+ }
+ else
+ $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 ($CONFIG['username_domain'] && !strstr($user, '@'))
+ {
+ if (is_array($CONFIG['username_domain']) && isset($CONFIG['username_domain'][$host]))
+ $user .= '@'.$CONFIG['username_domain'][$host];
+ else if (!empty($CONFIG['username_domain']))
+ $user .= '@'.$CONFIG['username_domain'];
+ }
+
+
+ // query if user already registered
+ $sql_result = $DB->query("SELECT user_id, username, language, preferences
+ FROM ".get_table_name('users')."
+ WHERE mail_host=? AND (username=? OR alias=?)",
+ $host,
+ $user,
+ $user);
+
+ // user already registered -> overwrite username
+ if ($sql_arr = $DB->fetch_assoc($sql_result))
+ {
+ $user_id = $sql_arr['user_id'];
+ $user = $sql_arr['username'];
+ }
+
+ // try to resolve email address from virtuser table
+ if (!empty($CONFIG['virtuser_file']) && strstr($user, '@'))
+ $user = rcmail_email2user($user);
+
+
+ // exit if IMAP login failed
+ if (!($imap_login = $IMAP->connect($host, $user, $pass, $imap_port, $imap_ssl)))
+ return FALSE;
+
+ // user already registered
+ if ($user_id && !empty($sql_arr))
+ {
+ // get user prefs
+ if (strlen($sql_arr['preferences']))
+ {
+ $user_prefs = unserialize($sql_arr['preferences']);
+ $_SESSION['user_prefs'] = $user_prefs;
+ array_merge($CONFIG, $user_prefs);
+ }
+
+
+ // set user specific language
+ if (strlen($sql_arr['language']))
+ $sess_user_lang = $_SESSION['user_lang'] = $sql_arr['language'];
+
+ // update user's record
+ $DB->query("UPDATE ".get_table_name('users')."
+ SET last_login=now()
+ WHERE user_id=?",
+ $user_id);
+ }
+ // create new system user
+ else if ($CONFIG['auto_create_user'])
+ {
+ $user_id = rcmail_create_user($user, $host);
+ }
+
+ if ($user_id)
+ {
+ $_SESSION['user_id'] = $user_id;
+ $_SESSION['imap_host'] = $host;
+ $_SESSION['imap_port'] = $imap_port;
+ $_SESSION['imap_ssl'] = $imap_ssl;
+ $_SESSION['username'] = $user;
+ $_SESSION['user_lang'] = $sess_user_lang;
+ $_SESSION['password'] = encrypt_passwd($pass);
+
+ // force reloading complete list of subscribed mailboxes
+ rcmail_set_imap_prop();
+ $IMAP->clear_cache('mailboxes');
+ $IMAP->create_default_folders();
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+// create new entry in users and identities table
+function rcmail_create_user($user, $host)
+ {
+ global $DB, $CONFIG, $IMAP;
+
+ $user_email = '';
+
+ // try to resolve user in virtusertable
+ if (!empty($CONFIG['virtuser_file']) && strstr($user, '@')==FALSE)
+ $user_email = rcmail_user2email($user);
+
+ $DB->query("INSERT INTO ".get_table_name('users')."
+ (created, last_login, username, mail_host, alias, language)
+ VALUES (now(), now(), ?, ?, ?, ?)",
+ $user,
+ $host,
+ $user_email,
+ $_SESSION['user_lang']);
+
+ if ($user_id = $DB->insert_id(get_sequence_name('users')))
+ {
+ $mail_domain = $host;
+ if (is_array($CONFIG['mail_domain']))
+ {
+ if (isset($CONFIG['mail_domain'][$host]))
+ $mail_domain = $CONFIG['mail_domain'][$host];
+ }
+ else if (!empty($CONFIG['mail_domain']))
+ $mail_domain = $CONFIG['mail_domain'];
+
+ if ($user_email=='')
+ $user_email = strstr($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
+
+ $user_name = $user!=$user_email ? $user : '';
+
+ // try to resolve the e-mail address from the virtuser table
+ if (!empty($CONFIG['virtuser_query']))
+ {
+ $sql_result = $DB->query(preg_replace('/%u/', $user, $CONFIG['virtuser_query']));
+ if ($sql_arr = $DB->fetch_array($sql_result))
+ $user_email = $sql_arr[0];
+ }
+
+ // also create new identity records
+ $DB->query("INSERT INTO ".get_table_name('identities')."
+ (user_id, del, standard, name, email)
+ VALUES (?, 0, 1, ?, ?)",
+ $user_id,
+ $user_name,
+ $user_email);
+
+
+ // get existing mailboxes
+ $a_mailboxes = $IMAP->list_mailboxes();
+ }
+ else
+ {
+ raise_error(array('code' => 500,
+ 'type' => 'php',
+ 'line' => __LINE__,
+ 'file' => __FILE__,
+ 'message' => "Failed to create new user"), TRUE, FALSE);
+ }
+
+ return $user_id;
+ }
+
+
+// load virtuser table in array
+function rcmail_getvirtualfile()
+ {
+ global $CONFIG;
+ if (empty($CONFIG['virtuser_file']) || !is_file($CONFIG['virtuser_file']))
+ return FALSE;
+
+ // read file
+ $a_lines = file($CONFIG['virtuser_file']);
+ return $a_lines;
+ }
+
+
+// find matches of the given pattern in virtuser table
+function rcmail_findinvirtual($pattern)
+ {
+ $result = array();
+ $virtual = rcmail_getvirtualfile();
+ if ($virtual==FALSE)
+ return $result;
+
+ // check each line for matches
+ foreach ($virtual as $line)
+ {
+ $line = trim($line);
+ if (empty($line) || $line{0}=='#')
+ continue;
+
+ if (eregi($pattern, $line))
+ $result[] = $line;
+ }
+
+ return $result;
+ }
+
+
+// resolve username with virtuser table
+function rcmail_email2user($email)
+ {
+ $user = $email;
+ $r = rcmail_findinvirtual("^$email");
+
+ for ($i=0; $i<count($r); $i++)
+ {
+ $data = $r[$i];
+ $arr = preg_split('/\s+/', $data);
+ if(count($arr)>0)
+ {
+ $user = trim($arr[count($arr)-1]);
+ break;
+ }
+ }
+
+ return $user;
+ }
+
+
+// resolve e-mail address with virtuser table
+function rcmail_user2email($user)
+ {
+ $email = "";
+ $r = rcmail_findinvirtual("$user$");
+
+ for ($i=0; $i<count($r); $i++)
+ {
+ $data=$r[$i];
+ $arr = preg_split('/\s+/', $data);
+ if (count($arr)>0)
+ {
+ $email = trim($arr[0]);
+ break;
+ }
+ }
+
+ return $email;
+ }
+
+
+function rcmail_save_user_prefs($a_user_prefs)
+ {
+ global $DB, $CONFIG, $sess_user_lang;
+
+ $DB->query("UPDATE ".get_table_name('users')."
+ SET preferences=?,
+ language=?
+ WHERE user_id=?",
+ serialize($a_user_prefs),
+ $sess_user_lang,
+ $_SESSION['user_id']);
+
+ if ($DB->affected_rows())
+ {
+ $_SESSION['user_prefs'] = $a_user_prefs;
+ $CONFIG = array_merge($CONFIG, $a_user_prefs);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+// overwrite action variable
+function rcmail_overwrite_action($action)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+ $GLOBALS['_action'] = $action;
+
+ $OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $action));
+ }
+
+
+function show_message($message, $type='notice', $vars=NULL)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME, $REMOTE_REQUEST;
+
+ $framed = $GLOBALS['_framed'];
+ $command = sprintf("display_message('%s', '%s');",
+ addslashes(rep_specialchars_output(rcube_label(array('name' => $message, 'vars' => $vars)))),
+ $type);
+
+ if ($REMOTE_REQUEST)
+ return 'this.'.$command;
+
+ else
+ $OUTPUT->add_script(sprintf("%s%s.%s\n",
+ $framed ? sprintf('if(parent.%s)parent.', $JS_OBJECT_NAME) : '',
+ $JS_OBJECT_NAME,
+ $command));
+
+ // console(rcube_label($message));
+ }
+
+
+function console($msg, $type=1)
+ {
+ if ($GLOBALS['REMOTE_REQUEST'])
+ print "// $msg\n";
+ else
+ {
+ print $msg;
+ print "\n<hr>\n";
+ }
+ }
+
+
+// encrypt IMAP password using DES encryption
+function encrypt_passwd($pass)
+ {
+ $cypher = des(get_des_key(), $pass, 1, 0, NULL);
+ return base64_encode($cypher);
+ }
+
+
+// decrypt IMAP password using DES encryption
+function decrypt_passwd($cypher)
+ {
+ $pass = des(get_des_key(), base64_decode($cypher), 0, 0, NULL);
+ return preg_replace('/\x00/', '', $pass);
+ }
+
+
+// return a 24 byte key for the DES encryption
+function get_des_key()
+ {
+ $key = !empty($GLOBALS['CONFIG']['des_key']) ? $GLOBALS['CONFIG']['des_key'] : 'rcmail?24BitPwDkeyF**ECB';
+ $len = strlen($key);
+
+ // make sure the key is exactly 24 chars long
+ if ($len<24)
+ $key .= str_repeat('_', 24-$len);
+ else if ($len>24)
+ substr($key, 0, 24);
+
+ return $key;
+ }
+
+
+// send correct response on a remote request
+function rcube_remote_response($js_code, $flush=FALSE)
+ {
+ global $OUTPUT, $CHARSET;
+ static $s_header_sent = FALSE;
+
+ if (!$s_header_sent)
+ {
+ $s_header_sent = TRUE;
+ send_nocacheing_headers();
+ header('Content-Type: application/x-javascript; charset='.$CHARSET);
+ print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n";
+ }
+
+ // send response code
+ print rcube_charset_convert($js_code, $CHARSET, $OUTPUT->get_charset());
+
+ if ($flush) // flush the output buffer
+ flush();
+ else // terminate script
+ exit;
+ }
+
+
+// send correctly formatted response for a request posted to an iframe
+function rcube_iframe_response($js_code='')
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!empty($js_code))
+ $OUTPUT->add_script("if(parent.$JS_OBJECT_NAME){\n" . $js_code . "\n}");
+
+ $OUTPUT->write();
+ exit;
+ }
+
+
+// read directory program/localization/ and return a list of available languages
+function rcube_list_languages()
+ {
+ global $CONFIG, $INSTALL_PATH;
+ 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 ? $label : $name;
+ }
+ closedir($dh);
+ }
+ }
+ return $sa_languages;
+ }
+
+
+// add a localized label to the client environment
+function rcube_add_label()
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ $arg_list = func_get_args();
+ foreach ($arg_list as $i => $name)
+ $OUTPUT->add_script(sprintf("%s.add_label('%s', '%s');",
+ $JS_OBJECT_NAME,
+ $name,
+ rep_specialchars_output(rcube_label($name), 'js')));
+ }
+
+
+// remove temp files of a session
+function rcmail_clear_session_temp($sess_id)
+ {
+ global $CONFIG;
+
+ $temp_dir = slashify($CONFIG['temp_dir']);
+ $cache_dir = $temp_dir.$sess_id;
+
+ if (is_dir($cache_dir))
+ {
+ clear_directory($cache_dir);
+ rmdir($cache_dir);
+ }
+ }
+
+
+// remove all expired message cache records
+function rcmail_message_cache_gc()
+ {
+ global $DB, $CONFIG;
+
+ // no cache lifetime configured
+ if (empty($CONFIG['message_cache_lifetime']))
+ return;
+
+ // get target timestamp
+ $ts = get_offset_time($CONFIG['message_cache_lifetime'], -1);
+
+ $DB->query("DELETE FROM ".get_table_name('messages')."
+ WHERE created < ".$DB->fromunixtime($ts));
+ }
+
+
+// convert a string from one charset to another
+// this function is not complete and not tested well
+function rcube_charset_convert($str, $from, $to=NULL)
+ {
+ global $MBSTRING, $MBSTRING_ENCODING;
+
+ $from = strtoupper($from);
+ $to = $to==NULL ? strtoupper($GLOBALS['CHARSET']) : strtoupper($to);
+
+ if ($from==$to)
+ return $str;
+
+ // convert charset using mbstring module
+ if ($MBSTRING)
+ {
+ $to = $to=="UTF-7" ? "UTF7-IMAP" : $to;
+ $from = $from=="UTF-7" ? "UTF7-IMAP": $from;
+
+ if (in_array($to, $MBSTRING_ENCODING) && in_array($from, $MBSTRING_ENCODING))
+ return mb_convert_encoding($str, $to, $from);
+ }
+
+ // convert charset using iconv module
+ if (function_exists('iconv') && $from!='UTF-7' && $to!='UTF-7')
+ return iconv($from, $to, $str);
+
+ $conv = new utf8();
+
+ // convert string to UTF-8
+ if ($from=='UTF-7')
+ $str = rcube_charset_convert(UTF7DecodeString($str), 'ISO-8859-1');
+ else if ($from=='ISO-8859-1' && function_exists('utf8_encode'))
+ $str = utf8_encode($str);
+ else if ($from!='UTF-8')
+ {
+ $conv->loadCharset($from);
+ $str = $conv->strToUtf8($str);
+ }
+
+ // encode string for output
+ if ($to=='UTF-7')
+ return UTF7EncodeString($str);
+ else if ($to=='ISO-8859-1' && function_exists('utf8_decode'))
+ return utf8_decode($str);
+ else if ($to!='UTF-8')
+ {
+ $conv->loadCharset($to);
+ return $conv->utf8ToStr($str);
+ }
+
+ // return UTF-8 string
+ return $str;
+ }
+
+
+
+// replace specials characters to a specific encoding type
+function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
+ {
+ global $OUTPUT_TYPE, $OUTPUT;
+ static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table;
+
+ if (!$enctype)
+ $enctype = $GLOBALS['OUTPUT_TYPE'];
+
+ // convert nbsps back to normal spaces if not html
+ if ($enctype!='html')
+ $str = str_replace(chr(160), ' ', $str);
+
+ // encode for plaintext
+ if ($enctype=='text')
+ return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
+
+ // encode for HTML output
+ if ($enctype=='html')
+ {
+ if (!$html_encode_arr)
+ {
+ $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);
+ unset($html_encode_arr['?']);
+ unset($html_encode_arr['&']);
+ }
+
+ $ltpos = strpos($str, '<');
+ $encode_arr = $html_encode_arr;
+
+ // don't replace quotes and html tags
+ if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
+ {
+ unset($encode_arr['"']);
+ unset($encode_arr['<']);
+ unset($encode_arr['>']);
+ }
+ else if ($mode=='remove')
+ $str = strip_tags($str);
+
+ $out = strtr($str, $encode_arr);
+
+ return $newlines ? nl2br($out) : $out;
+ }
+
+
+ if ($enctype=='url')
+ return rawurlencode($str);
+
+
+ // if the replace tables for RTF, XML and JS are not yet defined
+ if (!$js_rep_table)
+ {
+ $js_rep_table = $rtf_rep_table = $xml_rep_table = array();
+ $xml_rep_table['&'] = '&';
+
+ for ($c=160; $c<256; $c++) // can be increased to support more charsets
+ {
+ $hex = dechex($c);
+ $rtf_rep_table[Chr($c)] = "\\'$hex";
+ $xml_rep_table[Chr($c)] = "&#$c;";
+
+ if ($OUTPUT->get_charset()=='ISO-8859-1')
+ $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex);
+ }
+
+ $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34));
+ $xml_rep_table['"'] = '"';
+ }
+
+ // encode for RTF
+ if ($enctype=='xml')
+ return strtr($str, $xml_rep_table);
+
+ // encode for javascript use
+ if ($enctype=='js')
+ {
+ if ($OUTPUT->get_charset()!='UTF-8')
+ $str = rcube_charset_convert($str, $GLOBALS['CHARSET'], $OUTPUT->get_charset());
+
+ return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table));
+ }
+
+ // encode for RTF
+ if ($enctype=='rtf')
+ return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table));
+
+ // no encoding given -> return original string
+ return $str;
+ }
+
+
+/**
+ * Read input value and convert it for internal use
+ * Performs stripslashes() and charset conversion if necessary
+ *
+ * @param string Field name to read
+ * @param int Source to get value from (GPC)
+ * @param boolean Allow HTML tags in field value
+ * @param string Charset to convert into
+ * @return string Field value or NULL if not available
+ */
+function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
+ {
+ global $OUTPUT;
+ $value = NULL;
+
+ if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
+ $value = $_GET[$fname];
+ else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
+ $value = $_POST[$fname];
+ else if ($source==RCUBE_INPUT_GPC)
+ {
+ if (isset($_POST[$fname]))
+ $value = $_POST[$fname];
+ else if (isset($_GET[$fname]))
+ $value = $_GET[$fname];
+ else if (isset($_COOKIE[$fname]))
+ $value = $_COOKIE[$fname];
+ }
+
+ // strip slashes if magic_quotes enabled
+ if ((bool)get_magic_quotes_gpc())
+ $value = stripslashes($value);
+
+ // remove HTML tags if not allowed
+ if (!$allow_html)
+ $value = strip_tags($value);
+
+ // convert to internal charset
+ if (is_object($OUTPUT))
+ return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
+ else
+ return $value;
+ }
+
+/**
+ * Remove single and double quotes from given string
+ */
+function strip_quotes($str)
+{
+ return preg_replace('/[\'"]/', '', $str);
+}
+
+
+// ************** template parsing and gui functions **************
+
+
+// return boolean if a specific template exists
+function template_exists($name)
+ {
+ global $CONFIG, $OUTPUT;
+ $skin_path = $CONFIG['skin_path'];
+
+ // check template file
+ return is_file("$skin_path/templates/$name.html");
+ }
+
+
+// get page template an replace variable
+// similar function as used in nexImage
+function parse_template($name='main', $exit=TRUE)
+ {
+ global $CONFIG, $OUTPUT;
+ $skin_path = $CONFIG['skin_path'];
+
+ // read template file
+ $templ = '';
+ $path = "$skin_path/templates/$name.html";
+
+ if($fp = @fopen($path, 'r'))
+ {
+ $templ = fread($fp, filesize($path));
+ fclose($fp);
+ }
+ else
+ {
+ raise_error(array('code' => 500,
+ 'type' => 'php',
+ 'line' => __LINE__,
+ 'file' => __FILE__,
+ 'message' => "Error loading template for '$name'"), TRUE, TRUE);
+ return FALSE;
+ }
+
+
+ // parse for specialtags
+ $output = parse_rcube_xml($templ);
+
+ $OUTPUT->write(trim(parse_with_globals($output)), $skin_path);
+
+ if ($exit)
+ exit;
+ }
+
+
+
+// replace all strings ($varname) with the content of the according global variable
+function parse_with_globals($input)
+ {
+ $GLOBALS['__comm_path'] = $GLOBALS['COMM_PATH'];
+ $output = preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input);
+ return $output;
+ }
+
+
+
+function parse_rcube_xml($input)
+ {
+ $output = preg_replace('/<roundcube:([-_a-z]+)\s+([^>]+)>/Uie', "rcube_xml_command('\\1', '\\2')", $input);
+ return $output;
+ }
+
+
+function rcube_xml_command($command, $str_attrib, $add_attrib=array())
+ {
+ global $IMAP, $CONFIG, $OUTPUT;
+
+ $command = strtolower($command);
+ $attrib = parse_attrib_string($str_attrib) + $add_attrib;
+
+ // execute command
+ switch ($command)
+ {
+ // return a button
+ case 'button':
+ if ($attrib['command'])
+ return rcube_button($attrib);
+ break;
+
+ // show a label
+ case 'label':
+ if ($attrib['name'] || $attrib['command'])
+ return rep_specialchars_output(rcube_label($attrib));
+ break;
+
+ // create a menu item
+ case 'menu':
+ if ($attrib['command'] && $attrib['group'])
+ rcube_menu($attrib);
+ break;
+
+ // include a file
+ case 'include':
+ $path = realpath($CONFIG['skin_path'].$attrib['file']);
+
+ if($fp = @fopen($path, 'r'))
+ {
+ $incl = fread($fp, filesize($path));
+ fclose($fp);
+ return parse_rcube_xml($incl);
+ }
+ break;
+
+ // return code for a specific application object
+ case 'object':
+ $object = strtolower($attrib['name']);
+
+ $object_handlers = array(
+ // GENERAL
+ 'loginform' => 'rcmail_login_form',
+ 'username' => 'rcmail_current_username',
+
+ // MAIL
+ 'mailboxlist' => 'rcmail_mailbox_list',
+ 'message' => 'rcmail_message_container',
+ 'messages' => 'rcmail_message_list',
+ 'messagecountdisplay' => 'rcmail_messagecount_display',
+ 'quotadisplay' => 'rcmail_quota_display',
+ 'messageheaders' => 'rcmail_message_headers',
+ 'messagebody' => 'rcmail_message_body',
+ 'messageattachments' => 'rcmail_message_attachments',
+ 'blockedobjects' => 'rcmail_remote_objects_msg',
+ 'messagecontentframe' => 'rcmail_messagecontent_frame',
+ 'messagepartframe' => 'rcmail_message_part_frame',
+ 'messagepartcontrols' => 'rcmail_message_part_controls',
+ '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',
+ 'charsetselector' => 'rcmail_charset_selector',
+ 'searchform' => 'rcmail_search_form',
+ 'receiptcheckbox' => 'rcmail_receipt_checkbox',
+
+ // ADDRESS BOOK
+ 'addresslist' => 'rcmail_contacts_list',
+ 'addressframe' => 'rcmail_contact_frame',
+ 'recordscountdisplay' => 'rcmail_rowcount_display',
+ 'contactdetails' => 'rcmail_contact_details',
+ 'contacteditform' => 'rcmail_contact_editform',
+ 'ldappublicsearch' => 'rcmail_ldap_public_search_form',
+ 'ldappublicaddresslist' => 'rcmail_ldap_public_list',
+
+ // USER SETTINGS
+ 'userprefs' => 'rcmail_user_prefs_form',
+ 'itentitieslist' => 'rcmail_identities_list',
+ 'identityframe' => 'rcmail_identity_frame',
+ 'identityform' => 'rcube_identity_form',
+ 'foldersubscription' => 'rcube_subscription_form',
+ 'createfolder' => 'rcube_create_folder_form',
+ 'renamefolder' => 'rcube_rename_folder_form',
+ 'composebody' => 'rcmail_compose_body'
+ );
+
+
+ // execute object handler function
+ if ($object_handlers[$object] && function_exists($object_handlers[$object]))
+ return call_user_func($object_handlers[$object], $attrib);
+
+ else if ($object=='productname')
+ {
+ $name = !empty($CONFIG['product_name']) ? $CONFIG['product_name'] : 'RoundCube Webmail';
+ return rep_specialchars_output($name, 'html', 'all');
+ }
+ else if ($object=='version')
+ {
+ return (string)RCMAIL_VERSION;
+ }
+ else if ($object=='pagetitle')
+ {
+ $task = $GLOBALS['_task'];
+ $title = !empty($CONFIG['product_name']) ? $CONFIG['product_name'].' :: ' : '';
+
+ if ($task=='login')
+ $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $CONFIG['product_name'])));
+ else if ($task=='mail' && isset($GLOBALS['MESSAGE']['subject']))
+ $title .= $GLOBALS['MESSAGE']['subject'];
+ else if (isset($GLOBALS['PAGE_TITLE']))
+ $title .= $GLOBALS['PAGE_TITLE'];
+ else if ($task=='mail' && ($mbox_name = $IMAP->get_mailbox_name()))
+ $title .= rcube_charset_convert($mbox_name, 'UTF-7', 'UTF-8');
+ else
+ $title .= ucfirst($task);
+
+ return rep_specialchars_output($title, 'html', 'all');
+ }
+
+ break;
+ }
+
+ return '';
+ }
+
+
+// create and register a button
+function rcube_button($attrib)
+ {
+ global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $BROWSER, $COMM_PATH, $MAIN_TASKS;
+ static $sa_buttons = array();
+ static $s_button_count = 100;
+
+ // these commands can be called directly via url
+ $a_static_commands = array('compose', 'list');
+
+ $skin_path = $CONFIG['skin_path'];
+
+ if (!($attrib['command'] || $attrib['name']))
+ return '';
+
+ // try to find out the button type
+ if ($attrib['type'])
+ $attrib['type'] = strtolower($attrib['type']);
+ else
+ $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $arg['imageact']) ? 'image' : 'link';
+
+
+ $command = $attrib['command'];
+
+ // take the button from the stack
+ if($attrib['name'] && $sa_buttons[$attrib['name']])
+ $attrib = $sa_buttons[$attrib['name']];
+
+ // add button to button stack
+ else if($attrib['image'] || $arg['imageact'] || $attrib['imagepas'] || $attrib['class'])
+ {
+ if(!$attrib['name'])
+ $attrib['name'] = $command;
+
+ if (!$attrib['image'])
+ $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact'];
+
+ $sa_buttons[$attrib['name']] = $attrib;
+ }
+
+ // get saved button for this command/name
+ else if ($command && $sa_buttons[$command])
+ $attrib = $sa_buttons[$command];
+
+ //else
+ // return '';
+
+
+ // set border to 0 because of the link arround the button
+ if ($attrib['type']=='image' && !isset($attrib['border']))
+ $attrib['border'] = 0;
+
+ if (!$attrib['id'])
+ $attrib['id'] = sprintf('rcmbtn%d', $s_button_count++);
+
+ // get localized text for labels and titles
+ if ($attrib['title'])
+ $attrib['title'] = rep_specialchars_output(rcube_label($attrib['title']));
+ if ($attrib['label'])
+ $attrib['label'] = rep_specialchars_output(rcube_label($attrib['label']));
+
+ if ($attrib['alt'])
+ $attrib['alt'] = rep_specialchars_output(rcube_label($attrib['alt']));
+
+ // set title to alt attribute for IE browsers
+ if ($BROWSER['ie'] && $attrib['title'] && !$attrib['alt'])
+ {
+ $attrib['alt'] = $attrib['title'];
+ unset($attrib['title']);
+ }
+
+ // add empty alt attribute for XHTML compatibility
+ if (!isset($attrib['alt']))
+ $attrib['alt'] = '';
+
+
+ // register button in the system
+ if ($attrib['command'])
+ {
+ $OUTPUT->add_script(sprintf("%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');",
+ $JS_OBJECT_NAME,
+ $command,
+ $attrib['id'],
+ $attrib['type'],
+ $attrib['imageact'] ? $skin_path.$attrib['imageact'] : $attrib['classact'],
+ $attrib['imagesel'] ? $skin_path.$attrib['imagesel'] : $attrib['classsel'],
+ $attrib['imageover'] ? $skin_path.$attrib['imageover'] : ''));
+
+ // make valid href to specific buttons
+ if (in_array($attrib['command'], $MAIN_TASKS))
+ $attrib['href'] = htmlentities(ereg_replace('_task=[a-z]+', '_task='.$attrib['command'], $COMM_PATH));
+ else if (in_array($attrib['command'], $a_static_commands))
+ $attrib['href'] = htmlentities($COMM_PATH.'&_action='.$attrib['command']);
+ }
+
+ // overwrite attributes
+ if (!$attrib['href'])
+ $attrib['href'] = '#';
+
+ if ($command)
+ $attrib['onclick'] = sprintf("return %s.command('%s','%s',this)", $JS_OBJECT_NAME, $command, $attrib['prop']);
+
+ if ($command && $attrib['imageover'])
+ {
+ $attrib['onmouseover'] = sprintf("return %s.button_over('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']);
+ $attrib['onmouseout'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']);
+ }
+
+ if ($command && $attrib['imagesel'])
+ {
+ $attrib['onmousedown'] = sprintf("return %s.button_sel('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']);
+ $attrib['onmouseup'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']);
+ }
+
+ $out = '';
+
+ // generate image tag
+ if ($attrib['type']=='image')
+ {
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'align', 'alt'));
+ $img_tag = sprintf('<img src="%%s"%s />', $attrib_str);
+ $btn_content = sprintf($img_tag, $skin_path.$attrib['image']);
+ if ($attrib['label'])
+ $btn_content .= ' '.$attrib['label'];
+
+ $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title');
+ }
+ else if ($attrib['type']=='link')
+ {
+ $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command'];
+ $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style');
+ }
+ else if ($attrib['type']=='input')
+ {
+ $attrib['type'] = 'button';
+
+ if ($attrib['label'])
+ $attrib['value'] = $attrib['label'];
+
+ $attrib_str = create_attrib_string($attrib, array('type', 'value', 'onclick', 'id', 'class', 'style'));
+ $out = sprintf('<input%s disabled />', $attrib_str);
+ }
+
+ // generate html code for button
+ if ($btn_content)
+ {
+ $attrib_str = create_attrib_string($attrib, $link_attrib);
+ $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content);
+ }
+
+ return $out;
+ }
+
+
+function rcube_menu($attrib)
+ {
+
+ return '';
+ }
+
+
+
+function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
+ {
+ global $DB;
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+
+ $table = '<table' . $attrib_str . ">\n";
+
+ // add table title
+ $table .= "<thead><tr>\n";
+
+ foreach ($a_show_cols as $col)
+ $table .= '<td class="'.$col.'">' . rep_specialchars_output(rcube_label($col)) . "</td>\n";
+
+ $table .= "</tr></thead>\n<tbody>\n";
+
+ $c = 0;
+
+ if (!is_array($table_data))
+ {
+ while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data)))
+ {
+ $zebra_class = $c%2 ? 'even' : 'odd';
+
+ $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]);
+
+ // format each col
+ foreach ($a_show_cols as $col)
+ {
+ $cont = rep_specialchars_output($sql_arr[$col]);
+ $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
+ }
+
+ $table .= "</tr>\n";
+ $c++;
+ }
+ }
+ else
+ {
+ foreach ($table_data as $row_data)
+ {
+ $zebra_class = $c%2 ? 'even' : 'odd';
+
+ $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]);
+
+ // format each col
+ foreach ($a_show_cols as $col)
+ {
+ $cont = rep_specialchars_output($row_data[$col]);
+ $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
+ }
+
+ $table .= "</tr>\n";
+ $c++;
+ }
+ }
+
+ // complete message table
+ $table .= "</tbody></table>\n";
+
+ return $table;
+ }
+
+
+
+function rcmail_get_edit_field($col, $value, $attrib, $type='text')
+ {
+ $fname = '_'.$col;
+ $attrib['name'] = $fname;
+
+ if ($type=='checkbox')
+ {
+ $attrib['value'] = '1';
+ $input = new checkbox($attrib);
+ }
+ else if ($type=='textarea')
+ {
+ $attrib['cols'] = $attrib['size'];
+ $input = new textarea($attrib);
+ }
+ else
+ $input = new textfield($attrib);
+
+ // use value from post
+ if (!empty($_POST[$fname]))
+ $value = $_POST[$fname];
+
+ $out = $input->show($value);
+
+ return $out;
+ }
+
+
+// compose a valid attribute string for HTML tags
+function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
+ {
+ // allow the following attributes to be added to the <iframe> tag
+ $attrib_str = '';
+ foreach ($allowed_attribs as $a)
+ if (isset($attrib[$a]))
+ $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '"', $attrib[$a]));
+
+ return $attrib_str;
+ }
+
+
+// convert a HTML attribute string attributes to an associative array (name => value)
+function parse_attrib_string($str)
+ {
+ $attrib = array();
+ preg_match_all('/\s*([-_a-z]+)=["]([^"]+)["]?/i', stripslashes($str), $regs, PREG_SET_ORDER);
+
+ // convert attributes to an associative array (name => value)
+ if ($regs)
+ foreach ($regs as $attr)
+ $attrib[strtolower($attr[1])] = $attr[2];
+
+ return $attrib;
+ }
+
+
+function format_date($date, $format=NULL)
+ {
+ global $CONFIG, $sess_user_lang;
+
+ $ts = NULL;
+
+ if (is_numeric($date))
+ $ts = $date;
+ else if (!empty($date))
+ $ts = @strtotime($date);
+
+ if (empty($ts))
+ return '';
+
+ // get user's timezone
+ $tz = $CONFIG['timezone'];
+ if ($CONFIG['dst_active'])
+ $tz++;
+
+ // convert time to user's timezone
+ $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
+
+ // get current timestamp in user's timezone
+ $now = time(); // local time
+ $now -= (int)date('Z'); // make GMT time
+ $now += ($tz * 3600); // user's time
+ $now_date = getdate();
+
+ $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
+ $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
+
+ // define date format depending on current time
+ if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit)
+ return sprintf('%s %s', rcube_label('today'), date('H:i', $timestamp));
+ else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit)
+ $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
+ else if (!$format)
+ $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
+
+
+ // parse format string manually in order to provide localized weekday and month names
+ // an alternative would be to convert the date() format string to fit with strftime()
+ $out = '';
+ for($i=0; $i<strlen($format); $i++)
+ {
+ if ($format{$i}=='\\') // skip escape chars
+ continue;
+
+ // write char "as-is"
+ if ($format{$i}==' ' || $format{$i-1}=='\\')
+ $out .= $format{$i};
+ // weekday (short)
+ else if ($format{$i}=='D')
+ $out .= rcube_label(strtolower(date('D', $timestamp)));
+ // weekday long
+ else if ($format{$i}=='l')
+ $out .= rcube_label(strtolower(date('l', $timestamp)));
+ // month name (short)
+ else if ($format{$i}=='M')
+ $out .= rcube_label(strtolower(date('M', $timestamp)));
+ // month name (long)
+ else if ($format{$i}=='F')
+ $out .= rcube_label(strtolower(date('F', $timestamp)));
+ else
+ $out .= date($format{$i}, $timestamp);
+ }
+
+ return $out;
+ }
+
+
+// ************** functions delivering gui objects **************
+
+
+
+function rcmail_message_container($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmMessageContainer';
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+ $out = '<div' . $attrib_str . "></div>";
+
+ $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('message', '$attrib[id]');");
+
+ return $out;
+ }
+
+
+// return the IMAP username of the current session
+function rcmail_current_username($attrib)
+ {
+ global $DB;
+ static $s_username;
+
+ // alread fetched
+ if (!empty($s_username))
+ return $s_username;
+
+ // get e-mail address form default identity
+ $sql_result = $DB->query("SELECT email AS mailto
+ FROM ".get_table_name('identities')."
+ WHERE user_id=?
+ AND standard=1
+ AND del<>1",
+ $_SESSION['user_id']);
+
+ if ($DB->num_rows($sql_result))
+ {
+ $sql_arr = $DB->fetch_assoc($sql_result);
+ $s_username = $sql_arr['mailto'];
+ }
+ else if (strstr($_SESSION['username'], '@'))
+ $s_username = $_SESSION['username'];
+ else
+ $s_username = $_SESSION['username'].'@'.$_SESSION['imap_host'];
+
+ return $s_username;
+ }
+
+
+// return code for the webmail login form
+function rcmail_login_form($attrib)
+ {
+ global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD;
+
+ $labels = array();
+ $labels['user'] = rcube_label('username');
+ $labels['pass'] = rcube_label('password');
+ $labels['host'] = rcube_label('server');
+
+ $input_user = new textfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30));
+ $input_pass = new passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'size' => 30));
+ $input_action = new hiddenfield(array('name' => '_action', 'value' => 'login'));
+
+ $fields = array();
+ $fields['user'] = $input_user->show(get_input_value('_user', RCUBE_INPUT_POST));
+ $fields['pass'] = $input_pass->show();
+ $fields['action'] = $input_action->show();
+
+ if (is_array($CONFIG['default_host']))
+ {
+ $select_host = new select(array('name' => '_host', 'id' => 'rcmloginhost'));
+
+ foreach ($CONFIG['default_host'] as $key => $value)
+ $select_host->add($value, (is_numeric($key) ? $value : $key));
+
+ $fields['host'] = $select_host->show($_POST['_host']);
+ }
+ else if (!strlen($CONFIG['default_host']))
+ {
+ $input_host = new textfield(array('name' => '_host', 'id' => 'rcmloginhost', 'size' => 30));
+ $fields['host'] = $input_host->show($_POST['_host']);
+ }
+
+ $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form';
+ $form_start = !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : '';
+ $form_end = !strlen($attrib['form']) ? '</form>' : '';
+
+ if ($fields['host'])
+ $form_host = <<<EOF
+
+</tr><tr>
+
+<td class="title"><label for="rcmloginhost">$labels[host]</label></td>
+<td>$fields[host]</td>
+
+EOF;
+
+ $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('loginform', '$form_name');");
+
+ $out = <<<EOF
+$form_start
+$SESS_HIDDEN_FIELD
+$fields[action]
+<table><tr>
+
+<td class="title"><label for="rcmloginuser">$labels[user]</label></td>
+<td>$fields[user]</td>
+
+</tr><tr>
+
+<td class="title"><label for="rcmloginpwd">$labels[pass]</label></td>
+<td>$fields[pass]</td>
+$form_host
+</tr></table>
+$form_end
+EOF;
+
+ return $out;
+ }
+
+
+function rcmail_charset_selector($attrib)
+ {
+ global $OUTPUT;
+
+ // pass the following attributes to the form class
+ $field_attrib = array('name' => '_charset');
+ foreach ($attrib as $attr => $value)
+ if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
+ $field_attrib[$attr] = $value;
+
+ $charsets = array(
+ 'US-ASCII' => 'ASCII (English)',
+ 'EUC-JP' => 'EUC-JP (Japanese)',
+ 'EUC-KR' => 'EUC-KR (Korean)',
+ 'BIG5' => 'BIG5 (Chinese)',
+ 'GB2312' => 'GB2312 (Chinese)',
+ 'ISO-2022-JP' => 'ISO-2022-JP (Japanese)',
+ 'ISO-8859-1' => 'ISO-8859-1 (Latin-1)',
+ 'ISO-8859-2' => 'ISO-8895-2 (Central European)',
+ 'ISO-8859-7' => 'ISO-8859-7 (Greek)',
+ 'ISO-8859-9' => 'ISO-8859-9 (Turkish)',
+ 'Windows-1251' => 'Windows-1251 (Cyrillic)',
+ 'Windows-1252' => 'Windows-1252 (Western)',
+ 'Windows-1255' => 'Windows-1255 (Hebrew)',
+ 'Windows-1256' => 'Windows-1256 (Arabic)',
+ 'Windows-1257' => 'Windows-1257 (Baltic)',
+ 'UTF-8' => 'UTF-8'
+ );
+
+ $select = new select($field_attrib);
+ $select->add(array_values($charsets), array_keys($charsets));
+
+ $set = $_POST['_charset'] ? $_POST['_charset'] : $OUTPUT->get_charset();
+ return $select->show($set);
+ }
+
+
+/****** debugging function ********/
+
+function rcube_timer()
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
+ }
+
+
+function rcube_print_time($timer, $label='Timer')
+ {
+ static $print_count = 0;
+
+ $print_count++;
+ $now = rcube_timer();
+ $diff = $now-$timer;
+
+ if (empty($label))
+ $label = 'Timer '.$print_count;
+
+ console(sprintf("%s: %0.4f sec", $label, $diff));
+ }
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_db.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | PEAR:DB wrapper class that implements PEAR DB functions |
+ | See http://pear.php.net/package/DB |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: David Saez Padros <david@ols.es> |
+ | Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_db.inc 262 2006-06-25 09:17:07Z thomasb $
+
+*/
+
+
+/**
+ * Obtain the PEAR::DB class that is used for abstraction
+ */
+require_once('DB.php');
+
+
+/**
+ * Database independent query interface
+ *
+ * This is a wrapper for the PEAR::DB class
+ *
+ * @package RoundCube Webmail
+ * @author David Saez Padros <david@ols.es>
+ * @author Thomas Bruederli <roundcube@gmail.com>
+ * @version 1.17
+ * @link http://pear.php.net/package/DB
+ */
+class rcube_db
+ {
+ var $db_dsnw; // DSN for write operations
+ var $db_dsnr; // DSN for read operations
+ var $db_connected = false; // Already connected ?
+ var $db_mode = ''; // Connection mode
+ var $db_handle = 0; // Connection handle
+ var $db_pconn = false; // Use persistent connections
+ var $db_error = false;
+ var $db_error_msg = '';
+
+ var $a_query_results = array('dummy');
+ var $last_res_id = 0;
+
+
+ /**
+ * Object constructor
+ *
+ * @param string DSN for read/write operations
+ * @param string Optional DSN for read only operations
+ */
+ function __construct($db_dsnw, $db_dsnr='', $pconn=false)
+ {
+ if ($db_dsnr=='')
+ $db_dsnr=$db_dsnw;
+
+ $this->db_dsnw = $db_dsnw;
+ $this->db_dsnr = $db_dsnr;
+ $this->db_pconn = $pconn;
+
+ $dsn_array = DB::parseDSN($db_dsnw);
+ $this->db_provider = $dsn_array['phptype'];
+ }
+
+
+ /**
+ * PHP 4 object constructor
+ *
+ * @see rcube_db::__construct
+ */
+ function rcube_db($db_dsnw, $db_dsnr='', $pconn=false)
+ {
+ $this->__construct($db_dsnw, $db_dsnr, $pconn);
+ }
+
+
+ /**
+ * Connect to specific database
+ *
+ * @param string DSN for DB connections
+ * @return object PEAR database handle
+ * @access private
+ */
+ function dsn_connect($dsn)
+ {
+ // Use persistent connections if available
+ $dbh = DB::connect($dsn, array('persistent' => $this->db_pconn));
+
+ if (DB::isError($dbh))
+ {
+ $this->db_error = TRUE;
+ $this->db_error_msg = $dbh->getMessage();
+
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $this->db_error_msg), TRUE, FALSE);
+
+ return FALSE;
+ }
+
+ else if ($this->db_provider=='sqlite')
+ {
+ $dsn_array = DB::parseDSN($dsn);
+ if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials))
+ $this->_sqlite_create_database($dbh, $this->sqlite_initials);
+ }
+
+ return $dbh;
+ }
+
+
+ /**
+ * Connect to appropiate databse
+ * depending on the operation
+ *
+ * @param string Connection mode (r|w)
+ * @access public
+ */
+ function db_connect($mode)
+ {
+ $this->db_mode = $mode;
+
+ // Already connected
+ if ($this->db_connected)
+ {
+ // no replication, current connection is ok
+ if ($this->db_dsnw==$this->db_dsnr)
+ return;
+
+ // connected to master, current connection is ok
+ if ($this->db_mode=='w')
+ return;
+
+ // Same mode, current connection is ok
+ if ($this->db_mode==$mode)
+ return;
+ }
+
+ if ($mode=='r')
+ $dsn = $this->db_dsnr;
+ else
+ $dsn = $this->db_dsnw;
+
+ $this->db_handle = $this->dsn_connect($dsn);
+ $this->db_connected = $this->db_handle ? TRUE : FALSE;
+ }
+
+
+ /**
+ * Getter for error state
+ *
+ * @param boolean True on error
+ */
+ function is_error()
+ {
+ return $this->db_error ? $this->db_error_msg : FALSE;
+ }
+
+
+ /**
+ * Execute a SQL query
+ *
+ * @param string SQL query to execute
+ * @param mixed Values to be inserted in query
+ * @return number Query handle identifier
+ * @access public
+ */
+ function query()
+ {
+ $params = func_get_args();
+ $query = array_shift($params);
+
+ return $this->_query($query, 0, 0, $params);
+ }
+
+
+ /**
+ * Execute a SQL query with limits
+ *
+ * @param string SQL query to execute
+ * @param number Offset for LIMIT statement
+ * @param number Number of rows for LIMIT statement
+ * @param mixed Values to be inserted in query
+ * @return number Query handle identifier
+ * @access public
+ */
+ function limitquery()
+ {
+ $params = func_get_args();
+ $query = array_shift($params);
+ $offset = array_shift($params);
+ $numrows = array_shift($params);
+
+ return $this->_query($query, $offset, $numrows, $params);
+ }
+
+
+ /**
+ * Execute a SQL query with limits
+ *
+ * @param string SQL query to execute
+ * @param number Offset for LIMIT statement
+ * @param number Number of rows for LIMIT statement
+ * @param array Values to be inserted in query
+ * @return number Query handle identifier
+ * @access private
+ */
+ function _query($query, $offset, $numrows, $params)
+ {
+ // Read or write ?
+ if (strtolower(trim(substr($query,0,6)))=='select')
+ $mode='r';
+ else
+ $mode='w';
+
+ $this->db_connect($mode);
+
+ if (!$this->db_connected)
+ return FALSE;
+
+ if ($this->db_provider == 'sqlite')
+ $this->_sqlite_prepare();
+
+ if ($numrows || $offset)
+ $result = $this->db_handle->limitQuery($query,$offset,$numrows,$params);
+ else
+ $result = $this->db_handle->query($query, $params);
+
+ // add result, even if it's an error
+ return $this->_add_result($result);
+ }
+
+
+ /**
+ * Get number of rows for a SQL query
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Number of rows or FALSE on failure
+ * @access public
+ */
+ function num_rows($res_id=NULL)
+ {
+ if (!$this->db_handle)
+ return FALSE;
+
+ if ($result = $this->_get_result($res_id))
+ return $result->numRows();
+ else
+ return FALSE;
+ }
+
+
+ /**
+ * Get number of affected rows fort he last query
+ *
+ * @return mixed Number of rows or FALSE on failure
+ * @access public
+ */
+ function affected_rows()
+ {
+ if (!$this->db_handle)
+ return FALSE;
+
+ return $this->db_handle->affectedRows();
+ }
+
+
+ /**
+ * Get last inserted record ID
+ * For Postgres databases, a sequence name is required
+ *
+ * @param string Sequence name for increment
+ * @return mixed ID or FALSE on failure
+ * @access public
+ */
+ function insert_id($sequence = '')
+ {
+ if (!$this->db_handle || $this->db_mode=='r')
+ return FALSE;
+
+ switch($this->db_provider)
+ {
+ case 'pgsql':
+ // PostgreSQL uses sequences
+ $result = &$this->db_handle->getOne("SELECT CURRVAL('$sequence')");
+ if (DB::isError($result))
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $result->getMessage()), TRUE, FALSE);
+ }
+
+ return $result;
+
+ case 'mysql': // This is unfortuneate
+ return mysql_insert_id($this->db_handle->connection);
+
+ case 'mysqli':
+ return mysqli_insert_id($this->db_handle->connection);
+
+ case 'sqlite':
+ return sqlite_last_insert_rowid($this->db_handle->connection);
+
+ default:
+ die("portability issue with this database, please have the developer fix");
+ }
+ }
+
+
+ /**
+ * Get an associative array for one row
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access public
+ */
+ function fetch_assoc($res_id=NULL)
+ {
+ $result = $this->_get_result($res_id);
+ return $this->_fetch_row($result, DB_FETCHMODE_ASSOC);
+ }
+
+
+ /**
+ * Get an index array for one row
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access public
+ */
+ function fetch_array($res_id=NULL)
+ {
+ $result = $this->_get_result($res_id);
+ return $this->_fetch_row($result, DB_FETCHMODE_ORDERED);
+ }
+
+
+ /**
+ * Get co values for a result row
+ *
+ * @param object Query result handle
+ * @param number Fetch mode identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access private
+ */
+ function _fetch_row($result, $mode)
+ {
+ if (DB::isError($result))
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $this->db_link->getMessage()), TRUE, FALSE);
+ return FALSE;
+ }
+
+ return $result->fetchRow($mode);
+ }
+
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed Value to quote
+ * @return string Quoted/converted string for use in query
+ * @access public
+ */
+ function quote($input)
+ {
+ // create DB handle if not available
+ if (!$this->db_handle)
+ $this->db_connect('r');
+
+ // escape pear identifier chars
+ $rep_chars = array('?' => '\?',
+ '!' => '\!',
+ '&' => '\&');
+
+ return $this->db_handle->quoteSmart(strtr($input, $rep_chars));
+ }
+
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string Value to quote
+ * @return string Quoted string for use in query
+ * @deprecated Replaced by rcube_db::quote_identifier
+ * @see rcube_db::quote_identifier
+ * @access public
+ */
+ function quoteIdentifier($str)
+ {
+ return $this->quote_identifier($str);
+ }
+
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string Value to quote
+ * @return string Quoted string for use in query
+ * @access public
+ */
+ function quote_identifier($str)
+ {
+ if (!$this->db_handle)
+ $this->db_connect('r');
+
+ return $this->db_handle->quoteIdentifier($str);
+ }
+
+
+ /**
+ * Return SQL statement to convert a field value into a unix timestamp
+ *
+ * @param string Field name
+ * @return string SQL statement to use in query
+ * @access public
+ */
+ function unixtimestamp($field)
+ {
+ switch($this->db_provider)
+ {
+ case 'pgsql':
+ return "EXTRACT (EPOCH FROM $field)";
+ break;
+
+ default:
+ return "UNIX_TIMESTAMP($field)";
+ }
+ }
+
+
+ /**
+ * Return SQL statement to convert from a unix timestamp
+ *
+ * @param string Field name
+ * @return string SQL statement to use in query
+ * @access public
+ */
+ function fromunixtime($timestamp)
+ {
+ switch($this->db_provider)
+ {
+ case 'mysqli':
+ case 'mysql':
+ case 'sqlite':
+ return "FROM_UNIXTIME($timestamp)";
+
+ default:
+ return date("'Y-m-d H:i:s'", $timestamp);
+ }
+ }
+
+
+ /**
+ * Adds a query result and returns a handle ID
+ *
+ * @param object Query handle
+ * @return mixed Handle ID or FALE on failure
+ * @access private
+ */
+ function _add_result($res)
+ {
+ // sql error occured
+ if (DB::isError($res))
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 512)), TRUE, FALSE);
+ return FALSE;
+ }
+ else
+ {
+ $res_id = sizeof($this->a_query_results);
+ $this->a_query_results[$res_id] = $res;
+ $this->last_res_id = $res_id;
+ return $res_id;
+ }
+ }
+
+
+ /**
+ * Resolves a given handle ID and returns the according query handle
+ * If no ID is specified, the last ressource handle will be returned
+ *
+ * @param number Handle ID
+ * @return mixed Ressource handle or FALE on failure
+ * @access private
+ */
+ function _get_result($res_id=NULL)
+ {
+ if ($res_id==NULL)
+ $res_id = $this->last_res_id;
+
+ if ($res_id && isset($this->a_query_results[$res_id]))
+ return $this->a_query_results[$res_id];
+ else
+ return FALSE;
+ }
+
+
+ /**
+ * Create a sqlite database from a file
+ *
+ * @param object SQLite database handle
+ * @param string File path to use for DB creation
+ * @access private
+ */
+ function _sqlite_create_database($dbh, $file_name)
+ {
+ if (empty($file_name) || !is_string($file_name))
+ return;
+
+ $data = '';
+ if ($fd = fopen($file_name, 'r'))
+ {
+ $data = fread($fd, filesize($file_name));
+ fclose($fd);
+ }
+
+ if (strlen($data))
+ sqlite_exec($dbh->connection, $data);
+ }
+
+
+ /**
+ * Add some proprietary database functions to the current SQLite handle
+ * in order to make it MySQL compatible
+ *
+ * @access private
+ */
+ function _sqlite_prepare()
+ {
+ include_once('include/rcube_sqlite.inc');
+
+ // we emulate via callback some missing MySQL function
+ sqlite_create_function($this->db_handle->connection, "from_unixtime", "rcube_sqlite_from_unixtime");
+ sqlite_create_function($this->db_handle->connection, "unix_timestamp", "rcube_sqlite_unix_timestamp");
+ sqlite_create_function($this->db_handle->connection, "now", "rcube_sqlite_now");
+ sqlite_create_function($this->db_handle->connection, "md5", "rcube_sqlite_md5");
+ }
+
+
+ } // end class rcube_db
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_imap.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | IMAP wrapper that implements the Iloha IMAP Library (IIL) |
+ | See http://ilohamail.org/ for details |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_imap.inc 293 2006-08-04 13:56:08Z thomasb $
+
+*/
+
+
+/**
+ * Obtain classes from the Iloha IMAP library
+ */
+require_once('lib/imap.inc');
+require_once('lib/mime.inc');
+require_once('lib/utf7.inc');
+
+
+/**
+ * Interface class for accessing an IMAP server
+ *
+ * This is a wrapper that implements the Iloha IMAP Library (IIL)
+ *
+ * @package RoundCube Webmail
+ * @author Thomas Bruederli <roundcube@gmail.com>
+ * @version 1.31
+ * @link http://ilohamail.org
+ */
+class rcube_imap
+ {
+ var $db;
+ var $conn;
+ var $root_ns = '';
+ var $root_dir = '';
+ var $mailbox = 'INBOX';
+ var $list_page = 1;
+ var $page_size = 10;
+ var $sort_field = 'date';
+ var $sort_order = 'DESC';
+ var $delimiter = NULL;
+ var $caching_enabled = FALSE;
+ var $default_folders = array('INBOX');
+ var $default_folders_lc = array('inbox');
+ var $cache = array();
+ var $cache_keys = array();
+ var $cache_changes = array();
+ var $uid_id_map = array();
+ var $msg_headers = array();
+ var $capabilities = array();
+ var $skip_deleted = FALSE;
+ var $debug_level = 1;
+
+
+ /**
+ * Object constructor
+ *
+ * @param object Database connection
+ */
+ function __construct($db_conn)
+ {
+ $this->db = $db_conn;
+ }
+
+
+ /**
+ * PHP 4 object constructor
+ *
+ * @see rcube_imap::__construct
+ */
+ function rcube_imap($db_conn)
+ {
+ $this->__construct($db_conn);
+ }
+
+
+ /**
+ * Connect to an IMAP server
+ *
+ * @param string Host to connect
+ * @param string Username for IMAP account
+ * @param string Password for IMAP account
+ * @param number Port to connect to
+ * @param boolean Use SSL connection
+ * @return boolean TRUE on success, FALSE on failure
+ * @access public
+ */
+ function connect($host, $user, $pass, $port=143, $use_ssl=FALSE)
+ {
+ global $ICL_SSL, $ICL_PORT, $IMAP_USE_INTERNAL_DATE;
+
+ // check for Open-SSL support in PHP build
+ if ($use_ssl && in_array('openssl', get_loaded_extensions()))
+ $ICL_SSL = TRUE;
+ else if ($use_ssl)
+ {
+ raise_error(array('code' => 403, 'type' => 'imap', 'file' => __FILE__,
+ 'message' => 'Open SSL not available;'), TRUE, FALSE);
+ $port = 143;
+ }
+
+ $ICL_PORT = $port;
+ $IMAP_USE_INTERNAL_DATE = false;
+
+ $this->conn = iil_Connect($host, $user, $pass, array('imap' => 'check'));
+ $this->host = $host;
+ $this->user = $user;
+ $this->pass = $pass;
+ $this->port = $port;
+ $this->ssl = $use_ssl;
+
+ // print trace mesages
+ if ($this->conn && ($this->debug_level & 8))
+ console($this->conn->message);
+
+ // write error log
+ else if (!$this->conn && $GLOBALS['iil_error'])
+ {
+ raise_error(array('code' => 403,
+ 'type' => 'imap',
+ 'message' => $GLOBALS['iil_error']), TRUE, FALSE);
+ }
+
+ // get account namespace
+ if ($this->conn)
+ {
+ $this->_parse_capability($this->conn->capability);
+ iil_C_NameSpace($this->conn);
+
+ if (!empty($this->conn->delimiter))
+ $this->delimiter = $this->conn->delimiter;
+ if (!empty($this->conn->rootdir))
+ {
+ $this->set_rootdir($this->conn->rootdir);
+ $this->root_ns = ereg_replace('[\.\/]$', '', $this->conn->rootdir);
+ }
+ }
+
+ return $this->conn ? TRUE : FALSE;
+ }
+
+
+ /**
+ * Close IMAP connection
+ * Usually done on script shutdown
+ *
+ * @access public
+ */
+ function close()
+ {
+ if ($this->conn)
+ iil_Close($this->conn);
+ }
+
+
+ /**
+ * Close IMAP connection and re-connect
+ * This is used to avoid some strange socket errors when talking to Courier IMAP
+ *
+ * @access public
+ */
+ function reconnect()
+ {
+ $this->close();
+ $this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
+ }
+
+
+ /**
+ * Set a root folder for the IMAP connection.
+ *
+ * Only folders within this root folder will be displayed
+ * and all folder paths will be translated using this folder name
+ *
+ * @param string Root folder
+ * @access public
+ */
+ function set_rootdir($root)
+ {
+ if (ereg('[\.\/]$', $root)) //(substr($root, -1, 1)==='/')
+ $root = substr($root, 0, -1);
+
+ $this->root_dir = $root;
+
+ if (empty($this->delimiter))
+ $this->get_hierarchy_delimiter();
+ }
+
+
+ /**
+ * This list of folders will be listed above all other folders
+ *
+ * @param array Indexed list of folder names
+ * @access public
+ */
+ function set_default_mailboxes($arr)
+ {
+ if (is_array($arr))
+ {
+ $this->default_folders = $arr;
+ $this->default_folders_lc = array();
+
+ // add inbox if not included
+ if (!in_array_nocase('INBOX', $this->default_folders))
+ array_unshift($this->default_folders, 'INBOX');
+
+ // create a second list with lower cased names
+ foreach ($this->default_folders as $mbox)
+ $this->default_folders_lc[] = strtolower($mbox);
+ }
+ }
+
+
+ /**
+ * Set internal mailbox reference.
+ *
+ * All operations will be perfomed on this mailbox/folder
+ *
+ * @param string Mailbox/Folder name
+ * @access public
+ */
+ function set_mailbox($new_mbox)
+ {
+ $mailbox = $this->_mod_mailbox($new_mbox);
+
+ if ($this->mailbox == $mailbox)
+ return;
+
+ $this->mailbox = $mailbox;
+
+ // clear messagecount cache for this mailbox
+ $this->_clear_messagecount($mailbox);
+ }
+
+
+ /**
+ * Set internal list page
+ *
+ * @param number Page number to list
+ * @access public
+ */
+ function set_page($page)
+ {
+ $this->list_page = (int)$page;
+ }
+
+
+ /**
+ * Set internal page size
+ *
+ * @param number Number of messages to display on one page
+ * @access public
+ */
+ function set_pagesize($size)
+ {
+ $this->page_size = (int)$size;
+ }
+
+
+ /**
+ * Returns the currently used mailbox name
+ *
+ * @return string Name of the mailbox/folder
+ * @access public
+ */
+ function get_mailbox_name()
+ {
+ return $this->conn ? $this->_mod_mailbox($this->mailbox, 'out') : '';
+ }
+
+
+ /**
+ * Returns the IMAP server's capability
+ *
+ * @param string Capability name
+ * @return mixed Capability value or TRUE if supported, FALSE if not
+ * @access public
+ */
+ function get_capability($cap)
+ {
+ $cap = strtoupper($cap);
+ return $this->capabilities[$cap];
+ }
+
+
+ /**
+ * Returns the delimiter that is used by the IMAP server for folder separation
+ *
+ * @return string Delimiter string
+ * @access public
+ */
+ function get_hierarchy_delimiter()
+ {
+ if ($this->conn && empty($this->delimiter))
+ $this->delimiter = iil_C_GetHierarchyDelimiter($this->conn);
+
+ if (empty($this->delimiter))
+ $this->delimiter = '/';
+
+ return $this->delimiter;
+ }
+
+
+ /**
+ * Public method for mailbox listing.
+ *
+ * Converts mailbox name with root dir first
+ *
+ * @param string Optional root folder
+ * @param string Optional filter for mailbox listing
+ * @return array List of mailboxes/folders
+ * @access public
+ */
+ function list_mailboxes($root='', $filter='*')
+ {
+ $a_out = array();
+ $a_mboxes = $this->_list_mailboxes($root, $filter);
+
+ foreach ($a_mboxes as $mbox_row)
+ {
+ $name = $this->_mod_mailbox($mbox_row, 'out');
+ if (strlen($name))
+ $a_out[] = $name;
+ }
+
+ // INBOX should always be available
+ if (!in_array_nocase('INBOX', $a_out))
+ array_unshift($a_out, 'INBOX');
+
+ // sort mailboxes
+ $a_out = $this->_sort_mailbox_list($a_out);
+
+ return $a_out;
+ }
+
+
+ /**
+ * Private method for mailbox listing
+ *
+ * @return array List of mailboxes/folders
+ * @access private
+ * @see rcube_imap::list_mailboxes
+ */
+ function _list_mailboxes($root='', $filter='*')
+ {
+ $a_defaults = $a_out = array();
+
+ // get cached folder list
+ $a_mboxes = $this->get_cache('mailboxes');
+ if (is_array($a_mboxes))
+ return $a_mboxes;
+
+ // retrieve list of folders from IMAP server
+ $a_folders = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox($root), $filter);
+
+ if (!is_array($a_folders) || !sizeof($a_folders))
+ $a_folders = array();
+
+ // write mailboxlist to cache
+ $this->update_cache('mailboxes', $a_folders);
+
+ return $a_folders;
+ }
+
+
+ /**
+ * Get message count for a specific mailbox
+ *
+ * @param string Mailbox/folder name
+ * @param string Mode for count [ALL|UNSEEN|RECENT]
+ * @param boolean Force reading from server and update cache
+ * @return number Number of messages
+ * @access public
+ */
+ function messagecount($mbox_name='', $mode='ALL', $force=FALSE)
+ {
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ return $this->_messagecount($mailbox, $mode, $force);
+ }
+
+
+ /**
+ * Private method for getting nr of messages
+ *
+ * @access private
+ * @see rcube_imap::messagecount
+ */
+ function _messagecount($mailbox='', $mode='ALL', $force=FALSE)
+ {
+ $a_mailbox_cache = FALSE;
+ $mode = strtoupper($mode);
+
+ if (empty($mailbox))
+ $mailbox = $this->mailbox;
+
+ $a_mailbox_cache = $this->get_cache('messagecount');
+
+ // return cached value
+ if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode]))
+ return $a_mailbox_cache[$mailbox][$mode];
+
+ // RECENT count is fetched abit different
+ if ($mode == 'RECENT')
+ $count = iil_C_CheckForRecent($this->conn, $mailbox);
+
+ // use SEARCH for message counting
+ else if ($this->skip_deleted)
+ {
+ $search_str = "ALL UNDELETED";
+
+ // get message count and store in cache
+ if ($mode == 'UNSEEN')
+ $search_str .= " UNSEEN";
+
+ // get message count using SEARCH
+ // not very performant but more precise (using UNDELETED)
+ $count = 0;
+ $index = $this->_search_index($mailbox, $search_str);
+ if (is_array($index))
+ {
+ $str = implode(",", $index);
+ if (!empty($str))
+ $count = count($index);
+ }
+ }
+ else
+ {
+ if ($mode == 'UNSEEN')
+ $count = iil_C_CountUnseen($this->conn, $mailbox);
+ else
+ $count = iil_C_CountMessages($this->conn, $mailbox);
+ }
+
+ if (!is_array($a_mailbox_cache[$mailbox]))
+ $a_mailbox_cache[$mailbox] = array();
+
+ $a_mailbox_cache[$mailbox][$mode] = (int)$count;
+
+ // write back to cache
+ $this->update_cache('messagecount', $a_mailbox_cache);
+
+ return (int)$count;
+ }
+
+
+ /**
+ * Public method for listing headers
+ * convert mailbox name with root dir first
+ *
+ * @param string Mailbox/folder name
+ * @param number Current page to list
+ * @param string Header field to sort by
+ * @param string Sort order [ASC|DESC]
+ * @return array Indexed array with message header objects
+ * @access public
+ */
+ function list_headers($mbox_name='', $page=NULL, $sort_field=NULL, $sort_order=NULL)
+ {
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ return $this->_list_headers($mailbox, $page, $sort_field, $sort_order);
+ }
+
+
+ /**
+ * Private method for listing message headers
+ *
+ * @access private
+ * @see rcube_imap::list_headers
+ */
+ function _list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $recursive=FALSE)
+ {
+ if (!strlen($mailbox))
+ return array();
+
+ if ($sort_field!=NULL)
+ $this->sort_field = $sort_field;
+ if ($sort_order!=NULL)
+ $this->sort_order = strtoupper($sort_order);
+
+ $max = $this->_messagecount($mailbox);
+ $start_msg = ($this->list_page-1) * $this->page_size;
+
+ list($begin, $end) = $this->_get_message_range($max, $page);
+
+ // mailbox is empty
+ if ($begin >= $end)
+ return array();
+
+ $headers_sorted = FALSE;
+ $cache_key = $mailbox.'.msg';
+ $cache_status = $this->check_cache_status($mailbox, $cache_key);
+
+ // cache is OK, we can get all messages from local cache
+ if ($cache_status>0)
+ {
+ $a_msg_headers = $this->get_message_cache($cache_key, $start_msg, $start_msg+$this->page_size, $this->sort_field, $this->sort_order);
+ $headers_sorted = TRUE;
+ }
+ // cache is dirty, sync it
+ else if ($this->caching_enabled && $cache_status==-1 && !$recursive)
+ {
+ $this->sync_header_index($mailbox);
+ return $this->_list_headers($mailbox, $page, $this->sort_field, $this->sort_order, TRUE);
+ }
+ else
+ {
+ // retrieve headers from IMAP
+ if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')))
+ {
+ $msgs = $msg_index[$begin];
+ for ($i=$begin+1; $i < $end; $i++)
+ $msgs = $msgs.','.$msg_index[$i];
+ }
+ else
+ {
+ $msgs = sprintf("%d:%d", $begin+1, $end);
+
+ $i = 0;
+ for ($msg_seqnum = $begin; $msg_seqnum <= $end; $msg_seqnum++)
+ $msg_index[$i++] = $msg_seqnum;
+ }
+
+ // use this class for message sorting
+ $sorter = new rcube_header_sorter();
+ $sorter->set_sequence_numbers($msg_index);
+
+ // fetch reuested headers from server
+ $a_msg_headers = array();
+ $deleted_count = $this->_fetch_headers($mailbox, $msgs, $a_msg_headers, $cache_key);
+
+ // delete cached messages with a higher index than $max
+ $this->clear_message_cache($cache_key, $max);
+
+
+ // kick child process to sync cache
+ // ...
+
+ }
+
+
+ // return empty array if no messages found
+ if (!is_array($a_msg_headers) || empty($a_msg_headers))
+ return array();
+
+
+ // if not already sorted
+ if (!$headers_sorted)
+ {
+ $sorter->sort_headers($a_msg_headers);
+
+ if ($this->sort_order == 'DESC')
+ $a_msg_headers = array_reverse($a_msg_headers);
+ }
+
+ return array_values($a_msg_headers);
+ }
+
+
+
+ /**
+ * Public method for listing a specific set of headers
+ * convert mailbox name with root dir first
+ *
+ * @param string Mailbox/folder name
+ * @param array List of message ids to list
+ * @param number Current page to list
+ * @param string Header field to sort by
+ * @param string Sort order [ASC|DESC]
+ * @return array Indexed array with message header objects
+ * @access public
+ */
+ function list_header_set($mbox_name='', $msgs, $page=NULL, $sort_field=NULL, $sort_order=NULL)
+ {
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ return $this->_list_header_set($mailbox, $msgs, $page, $sort_field, $sort_order);
+ }
+
+
+ /**
+ * Private method for listing a set of message headers
+ *
+ * @access private
+ * @see rcube_imap::list_header_set
+ */
+ function _list_header_set($mailbox, $msgs, $page=NULL, $sort_field=NULL, $sort_order=NULL)
+ {
+ // also accept a comma-separated list of message ids
+ if (is_string($msgs))
+ $msgs = split(',', $msgs);
+
+ if (!strlen($mailbox) || empty($msgs))
+ return array();
+
+ if ($sort_field!=NULL)
+ $this->sort_field = $sort_field;
+ if ($sort_order!=NULL)
+ $this->sort_order = strtoupper($sort_order);
+
+ $max = count($msgs);
+ $start_msg = ($this->list_page-1) * $this->page_size;
+
+ // fetch reuested headers from server
+ $a_msg_headers = array();
+ $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL);
+
+ // return empty array if no messages found
+ if (!is_array($a_msg_headers) || empty($a_msg_headers))
+ return array();
+
+ // if not already sorted
+ $a_msg_headers = iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order);
+
+ // only return the requested part of the set
+ return array_slice(array_values($a_msg_headers), $start_msg, min($max-$start_msg, $this->page_size));
+ }
+
+
+ /**
+ * Helper function to get first and last index of the requested set
+ *
+ * @param number message count
+ * @param mixed page number to show, or string 'all'
+ * @return array array with two values: first index, last index
+ * @access private
+ */
+ function _get_message_range($max, $page)
+ {
+ $start_msg = ($this->list_page-1) * $this->page_size;
+
+ if ($page=='all')
+ {
+ $begin = 0;
+ $end = $max;
+ }
+ else if ($this->sort_order=='DESC')
+ {
+ $begin = $max - $this->page_size - $start_msg;
+ $end = $max - $start_msg;
+ }
+ else
+ {
+ $begin = $start_msg;
+ $end = $start_msg + $this->page_size;
+ }
+
+ if ($begin < 0) $begin = 0;
+ if ($end < 0) $end = $max;
+ if ($end > $max) $end = $max;
+
+ return array($begin, $end);
+ }
+
+
+
+ /**
+ * Fetches message headers
+ * Used for loop
+ *
+ * @param string Mailbox name
+ * @param string Message index to fetch
+ * @param array Reference to message headers array
+ * @param array Array with cache index
+ * @return number Number of deleted messages
+ * @access private
+ */
+ function _fetch_headers($mailbox, $msgs, &$a_msg_headers, $cache_key)
+ {
+ // cache is incomplete
+ $cache_index = $this->get_message_cache_index($cache_key);
+
+ // fetch reuested headers from server
+ $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs);
+ $deleted_count = 0;
+
+ if (!empty($a_header_index))
+ {
+ foreach ($a_header_index as $i => $headers)
+ {
+ if ($headers->deleted && $this->skip_deleted)
+ {
+ // delete from cache
+ if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid)
+ $this->remove_message_cache($cache_key, $headers->id);
+
+ $deleted_count++;
+ continue;
+ }
+
+ // add message to cache
+ if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid)
+ $this->add_message_cache($cache_key, $headers->id, $headers);
+
+ $a_msg_headers[$headers->uid] = $headers;
+ }
+ }
+
+ return $deleted_count;
+ }
+
+
+ // return sorted array of message UIDs
+ function message_index($mbox_name='', $sort_field=NULL, $sort_order=NULL)
+ {
+ if ($sort_field!=NULL)
+ $this->sort_field = $sort_field;
+ if ($sort_order!=NULL)
+ $this->sort_order = strtoupper($sort_order);
+
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ $key = "$mbox:".$this->sort_field.":".$this->sort_order.".msgi";
+
+ // have stored it in RAM
+ if (isset($this->cache[$key]))
+ return $this->cache[$key];
+
+ // check local cache
+ $cache_key = $mailbox.'.msg';
+ $cache_status = $this->check_cache_status($mailbox, $cache_key);
+
+ // cache is OK
+ if ($cache_status>0)
+ {
+ $a_index = $this->get_message_cache_index($cache_key, TRUE, $this->sort_field, $this->sort_order);
+ return array_values($a_index);
+ }
+
+
+ // fetch complete message index
+ $msg_count = $this->_messagecount($mailbox);
+ if ($this->get_capability('sort') && ($a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, '', TRUE)))
+ {
+ if ($this->sort_order == 'DESC')
+ $a_index = array_reverse($a_index);
+
+ $this->cache[$key] = $a_index;
+
+ }
+ else
+ {
+ $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", $this->sort_field);
+ $a_uids = iil_C_FetchUIDs($this->conn, $mailbox);
+
+ if ($this->sort_order=="ASC")
+ asort($a_index);
+ else if ($this->sort_order=="DESC")
+ arsort($a_index);
+
+ $i = 0;
+ $this->cache[$key] = array();
+ foreach ($a_index as $index => $value)
+ $this->cache[$key][$i++] = $a_uids[$index];
+ }
+
+ return $this->cache[$key];
+ }
+
+
+ function sync_header_index($mailbox)
+ {
+ $cache_key = $mailbox.'.msg';
+ $cache_index = $this->get_message_cache_index($cache_key);
+ $msg_count = $this->_messagecount($mailbox);
+
+ // fetch complete message index
+ $a_message_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", 'UID');
+
+ foreach ($a_message_index as $id => $uid)
+ {
+ // message in cache at correct position
+ if ($cache_index[$id] == $uid)
+ {
+// console("$id / $uid: OK");
+ unset($cache_index[$id]);
+ continue;
+ }
+
+ // message in cache but in wrong position
+ if (in_array((string)$uid, $cache_index, TRUE))
+ {
+// console("$id / $uid: Moved");
+ unset($cache_index[$id]);
+ }
+
+ // other message at this position
+ if (isset($cache_index[$id]))
+ {
+// console("$id / $uid: Delete");
+ $this->remove_message_cache($cache_key, $id);
+ unset($cache_index[$id]);
+ }
+
+
+// console("$id / $uid: Add");
+
+ // fetch complete headers and add to cache
+ $headers = iil_C_FetchHeader($this->conn, $mailbox, $id);
+ $this->add_message_cache($cache_key, $headers->id, $headers);
+ }
+
+ // those ids that are still in cache_index have been deleted
+ if (!empty($cache_index))
+ {
+ foreach ($cache_index as $id => $uid)
+ $this->remove_message_cache($cache_key, $id);
+ }
+ }
+
+
+ /**
+ * Invoke search request to IMAP server
+ *
+ * @param string mailbox name to search in
+ * @param string search criteria (ALL, TO, FROM, SUBJECT, etc)
+ * @param string search string
+ * @return array search results as list of message ids
+ * @access public
+ */
+ function search($mbox_name='', $criteria='ALL', $str=NULL, $charset=NULL)
+ {
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ if ($str && $criteria)
+ {
+ $search = (!empty($charset) ? "CHARSET $charset " : '') . sprintf("%s {%d}\r\n%s", $criteria, strlen($str), $str);
+ $results = $this->_search_index($mailbox, $search);
+
+ // try search without charset (probably not supported by server)
+ if (empty($results))
+ $results = $this->_search_index($mailbox, "$criteria $str");
+
+ return $results;
+ }
+ else
+ return $this->_search_index($mailbox, $criteria);
+ }
+
+
+ /**
+ * Private search method
+ *
+ * @return array search results as list of message ids
+ * @access private
+ * @see rcube_imap::search()
+ */
+ function _search_index($mailbox, $criteria='ALL')
+ {
+ $a_messages = iil_C_Search($this->conn, $mailbox, $criteria);
+ // clean message list (there might be some empty entries)
+ if (is_array($a_messages))
+ {
+ foreach ($a_messages as $i => $val)
+ if (empty($val))
+ unset($a_messages[$i]);
+ }
+
+ return $a_messages;
+ }
+
+
+ function get_headers($id, $mbox_name=NULL, $is_uid=TRUE)
+ {
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+
+ // get cached headers
+ if ($is_uid && ($headers = $this->get_cached_message($mailbox.'.msg', $id)))
+ return $headers;
+
+ $msg_id = $is_uid ? $this->_uid2id($id) : $id;
+ $headers = iil_C_FetchHeader($this->conn, $mailbox, $msg_id);
+
+ // write headers cache
+ if ($headers)
+ $this->add_message_cache($mailbox.'.msg', $msg_id, $headers);
+
+ return $headers;
+ }
+
+
+ function get_body($uid, $part=1)
+ {
+ if (!($msg_id = $this->_uid2id($uid)))
+ return FALSE;
+
+ $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
+ $structure = iml_GetRawStructureArray($structure_str);
+ $body = iil_C_FetchPartBody($this->conn, $this->mailbox, $msg_id, $part);
+
+ $encoding = iml_GetPartEncodingCode($structure, $part);
+
+ if ($encoding==3) $body = $this->mime_decode($body, 'base64');
+ else if ($encoding==4) $body = $this->mime_decode($body, 'quoted-printable');
+
+ return $body;
+ }
+
+
+ function get_raw_body($uid)
+ {
+ if (!($msg_id = $this->_uid2id($uid)))
+ return FALSE;
+
+ $body = iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
+ $body .= iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 1);
+
+ return $body;
+ }
+
+
+ // set message flag to one or several messages
+ // possible flags are: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT
+ function set_flag($uids, $flag)
+ {
+ $flag = strtoupper($flag);
+ $msg_ids = array();
+ if (!is_array($uids))
+ $uids = explode(',',$uids);
+
+ foreach ($uids as $uid) {
+ $msg_ids[$uid] = $this->_uid2id($uid);
+ }
+
+ if ($flag=='UNDELETED')
+ $result = iil_C_Undelete($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
+ else if ($flag=='UNSEEN')
+ $result = iil_C_Unseen($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
+ else
+ $result = iil_C_Flag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), $flag);
+
+ // reload message headers if cached
+ $cache_key = $this->mailbox.'.msg';
+ if ($this->caching_enabled)
+ {
+ foreach ($msg_ids as $uid => $id)
+ {
+ if ($cached_headers = $this->get_cached_message($cache_key, $uid))
+ {
+ $this->remove_message_cache($cache_key, $id);
+ //$this->get_headers($uid);
+ }
+ }
+
+ // close and re-open connection
+ // this prevents connection problems with Courier
+ $this->reconnect();
+ }
+
+ // set nr of messages that were flaged
+ $count = count($msg_ids);
+
+ // clear message count cache
+ if ($result && $flag=='SEEN')
+ $this->_set_messagecount($this->mailbox, 'UNSEEN', $count*(-1));
+ else if ($result && $flag=='UNSEEN')
+ $this->_set_messagecount($this->mailbox, 'UNSEEN', $count);
+ else if ($result && $flag=='DELETED')
+ $this->_set_messagecount($this->mailbox, 'ALL', $count*(-1));
+
+ return $result;
+ }
+
+
+ // append a mail message (source) to a specific mailbox
+ function save_message($mbox_name, &$message)
+ {
+ $mbox_name = stripslashes($mbox_name);
+ $mailbox = $this->_mod_mailbox($mbox_name);
+
+ // make sure mailbox exists
+ if (in_array($mailbox, $this->_list_mailboxes()))
+ $saved = iil_C_Append($this->conn, $mailbox, $message);
+
+ if ($saved)
+ {
+ // increase messagecount of the target mailbox
+ $this->_set_messagecount($mailbox, 'ALL', 1);
+ }
+
+ return $saved;
+ }
+
+
+ // move a message from one mailbox to another
+ function move_message($uids, $to_mbox, $from_mbox='')
+ {
+ $to_mbox = stripslashes($to_mbox);
+ $from_mbox = stripslashes($from_mbox);
+ $to_mbox = $this->_mod_mailbox($to_mbox);
+ $from_mbox = $from_mbox ? $this->_mod_mailbox($from_mbox) : $this->mailbox;
+
+ // make sure mailbox exists
+ if (!in_array($to_mbox, $this->_list_mailboxes()))
+ {
+ if (in_array(strtolower($to_mbox), $this->default_folders))
+ $this->create_mailbox($to_mbox, TRUE);
+ else
+ return FALSE;
+ }
+
+ // convert the list of uids to array
+ $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
+
+ // exit if no message uids are specified
+ if (!is_array($a_uids))
+ return false;
+
+ // convert uids to message ids
+ $a_mids = array();
+ foreach ($a_uids as $uid)
+ $a_mids[] = $this->_uid2id($uid, $from_mbox);
+
+ $moved = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox);
+
+ // send expunge command in order to have the moved message
+ // really deleted from the source mailbox
+ if ($moved)
+ {
+ $this->_expunge($from_mbox, FALSE);
+ $this->_clear_messagecount($from_mbox);
+ $this->_clear_messagecount($to_mbox);
+ }
+
+ // update cached message headers
+ $cache_key = $from_mbox.'.msg';
+ if ($moved && ($a_cache_index = $this->get_message_cache_index($cache_key)))
+ {
+ $start_index = 100000;
+ foreach ($a_uids as $uid)
+ {
+ if(($index = array_search($uid, $a_cache_index)) !== FALSE)
+ $start_index = min($index, $start_index);
+ }
+
+ // clear cache from the lowest index on
+ $this->clear_message_cache($cache_key, $start_index);
+ }
+
+ return $moved;
+ }
+
+
+ // mark messages as deleted and expunge mailbox
+ function delete_message($uids, $mbox_name='')
+ {
+ $mbox_name = stripslashes($mbox_name);
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+
+ // convert the list of uids to array
+ $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
+
+ // exit if no message uids are specified
+ if (!is_array($a_uids))
+ return false;
+
+
+ // convert uids to message ids
+ $a_mids = array();
+ foreach ($a_uids as $uid)
+ $a_mids[] = $this->_uid2id($uid, $mailbox);
+
+ $deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_mids));
+
+ // send expunge command in order to have the deleted message
+ // really deleted from the mailbox
+ if ($deleted)
+ {
+ $this->_expunge($mailbox, FALSE);
+ $this->_clear_messagecount($mailbox);
+ }
+
+ // remove deleted messages from cache
+ $cache_key = $mailbox.'.msg';
+ if ($deleted && ($a_cache_index = $this->get_message_cache_index($cache_key)))
+ {
+ $start_index = 100000;
+ foreach ($a_uids as $uid)
+ {
+ $index = array_search($uid, $a_cache_index);
+ $start_index = min($index, $start_index);
+ }
+
+ // clear cache from the lowest index on
+ $this->clear_message_cache($cache_key, $start_index);
+ }
+
+ return $deleted;
+ }
+
+
+ // clear all messages in a specific mailbox
+ function clear_mailbox($mbox_name=NULL)
+ {
+ $mbox_name = stripslashes($mbox_name);
+ $mailbox = !empty($mbox_name) ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ $msg_count = $this->_messagecount($mailbox, 'ALL');
+
+ if ($msg_count>0)
+ {
+ $cleared = iil_C_ClearFolder($this->conn, $mailbox);
+
+ // make sure the message count cache is cleared as well
+ if ($cleared)
+ {
+ $this->clear_message_cache($mailbox.'.msg');
+ $a_mailbox_cache = $this->get_cache('messagecount');
+ unset($a_mailbox_cache[$mailbox]);
+ $this->update_cache('messagecount', $a_mailbox_cache);
+ }
+
+ return $cleared;
+ }
+ else
+ return 0;
+ }
+
+
+ // send IMAP expunge command and clear cache
+ function expunge($mbox_name='', $clear_cache=TRUE)
+ {
+ $mbox_name = stripslashes($mbox_name);
+ $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
+ return $this->_expunge($mailbox, $clear_cache);
+ }
+
+
+ // send IMAP expunge command and clear cache
+ function _expunge($mailbox, $clear_cache=TRUE)
+ {
+ $result = iil_C_Expunge($this->conn, $mailbox);
+
+ if ($result>=0 && $clear_cache)
+ {
+ //$this->clear_message_cache($mailbox.'.msg');
+ $this->_clear_messagecount($mailbox);
+ }
+
+ return $result;
+ }
+
+
+ /* --------------------------------
+ * folder managment
+ * --------------------------------*/
+
+
+ /**
+ * Get a list of all folders available on the IMAP server
+ *
+ * @param string IMAP root dir
+ * @return array Inbdexed array with folder names
+ */
+ function list_unsubscribed($root='')
+ {
+ static $sa_unsubscribed;
+
+ if (is_array($sa_unsubscribed))
+ return $sa_unsubscribed;
+
+ // retrieve list of folders from IMAP server
+ $a_mboxes = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox($root), '*');
+
+ // modify names with root dir
+ foreach ($a_mboxes as $mbox_name)
+ {
+ $name = $this->_mod_mailbox($mbox_name, 'out');
+ if (strlen($name))
+ $a_folders[] = $name;
+ }
+
+ // filter folders and sort them
+ $sa_unsubscribed = $this->_sort_mailbox_list($a_folders);
+ return $sa_unsubscribed;
+ }
+
+
+ /**
+ * Get quota
+ * added by Nuny
+ */
+ function get_quota()
+ {
+ if ($this->get_capability('QUOTA'))
+ {
+ $result = iil_C_GetQuota($this->conn);
+ if ($result["total"])
+ return sprintf("%.2fMB / %.2fMB (%.0f%%)", $result["used"] / 1000.0, $result["total"] / 1000.0, $result["percent"]);
+ }
+
+ return FALSE;
+ }
+
+
+ /**
+ * subscribe to a specific mailbox(es)
+ */
+ function subscribe($mbox_name, $mode='subscribe')
+ {
+ if (is_array($mbox_name))
+ $a_mboxes = $mbox_name;
+ else if (is_string($mbox_name) && strlen($mbox_name))
+ $a_mboxes = explode(',', $mbox_name);
+
+ // let this common function do the main work
+ return $this->_change_subscription($a_mboxes, 'subscribe');
+ }
+
+
+ /**
+ * unsubscribe mailboxes
+ */
+ function unsubscribe($mbox_name)
+ {
+ if (is_array($mbox_name))
+ $a_mboxes = $mbox_name;
+ else if (is_string($mbox_name) && strlen($mbox_name))
+ $a_mboxes = explode(',', $mbox_name);
+
+ // let this common function do the main work
+ return $this->_change_subscription($a_mboxes, 'unsubscribe');
+ }
+
+
+ /**
+ * create a new mailbox on the server and register it in local cache
+ */
+ function create_mailbox($name, $subscribe=FALSE)
+ {
+ $result = FALSE;
+
+ // replace backslashes
+ $name = preg_replace('/[\\\]+/', '-', $name);
+
+ $name_enc = UTF7EncodeString($name);
+
+ // reduce mailbox name to 100 chars
+ $name_enc = substr($name_enc, 0, 100);
+
+ $abs_name = $this->_mod_mailbox($name_enc);
+ $a_mailbox_cache = $this->get_cache('mailboxes');
+
+ if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array_nocase($abs_name, $a_mailbox_cache)))
+ $result = iil_C_CreateFolder($this->conn, $abs_name);
+
+ // try to subscribe it
+ if ($subscribe)
+ $this->subscribe($name_enc);
+
+ return $result ? $name : FALSE;
+ }
+
+
+ /**
+ * set a new name to an existing mailbox
+ */
+ function rename_mailbox($mbox_name, $new_name)
+ {
+ $result = FALSE;
+
+ // replace backslashes
+ $name = preg_replace('/[\\\]+/', '-', $new_name);
+
+ // encode mailbox name and reduce it to 100 chars
+ $name_enc = substr(UTF7EncodeString($new_name), 0, 100);
+
+ // make absolute path
+ $mailbox = $this->_mod_mailbox($mbox_name);
+ $abs_name = $this->_mod_mailbox($name_enc);
+
+ if (strlen($abs_name))
+ $result = iil_C_RenameFolder($this->conn, $mailbox, $abs_name);
+
+ // clear cache
+ if ($result)
+ {
+ $this->clear_message_cache($mailbox.'.msg');
+ $this->clear_cache('mailboxes');
+ }
+
+ return $result ? $name : FALSE;
+ }
+
+
+ /**
+ * remove mailboxes from server
+ */
+ function delete_mailbox($mbox_name)
+ {
+ $deleted = FALSE;
+
+ if (is_array($mbox_name))
+ $a_mboxes = $mbox_name;
+ else if (is_string($mbox_name) && strlen($mbox_name))
+ $a_mboxes = explode(',', $mbox_name);
+
+ if (is_array($a_mboxes))
+ foreach ($a_mboxes as $mbox_name)
+ {
+ $mailbox = $this->_mod_mailbox($mbox_name);
+
+ // unsubscribe mailbox before deleting
+ iil_C_UnSubscribe($this->conn, $mailbox);
+
+ // send delete command to server
+ $result = iil_C_DeleteFolder($this->conn, $mailbox);
+ if ($result>=0)
+ $deleted = TRUE;
+ }
+
+ // clear mailboxlist cache
+ if ($deleted)
+ {
+ $this->clear_message_cache($mailbox.'.msg');
+ $this->clear_cache('mailboxes');
+ }
+
+ return $deleted;
+ }
+
+
+ /**
+ * Create all folders specified as default
+ */
+ function create_default_folders()
+ {
+ $a_folders = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox(''), '*');
+ $a_subscribed = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox(''), '*');
+
+ // create default folders if they do not exist
+ foreach ($this->default_folders as $folder)
+ {
+ $abs_name = $this->_mod_mailbox($folder);
+ if (!in_array_nocase($abs_name, $a_subscribed))
+ {
+ if (!in_array_nocase($abs_name, $a_folders))
+ $this->create_mailbox($folder, TRUE);
+ else
+ $this->subscribe($folder);
+ }
+ }
+ }
+
+
+
+ /* --------------------------------
+ * internal caching methods
+ * --------------------------------*/
+
+
+ function set_caching($set)
+ {
+ if ($set && is_object($this->db))
+ $this->caching_enabled = TRUE;
+ else
+ $this->caching_enabled = FALSE;
+ }
+
+
+ function get_cache($key)
+ {
+ // read cache
+ if (!isset($this->cache[$key]) && $this->caching_enabled)
+ {
+ $cache_data = $this->_read_cache_record('IMAP.'.$key);
+ $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE;
+ }
+
+ return $this->cache[$key];
+ }
+
+
+ function update_cache($key, $data)
+ {
+ $this->cache[$key] = $data;
+ $this->cache_changed = TRUE;
+ $this->cache_changes[$key] = TRUE;
+ }
+
+
+ function write_cache()
+ {
+ if ($this->caching_enabled && $this->cache_changed)
+ {
+ foreach ($this->cache as $key => $data)
+ {
+ if ($this->cache_changes[$key])
+ $this->_write_cache_record('IMAP.'.$key, serialize($data));
+ }
+ }
+ }
+
+
+ function clear_cache($key=NULL)
+ {
+ if ($key===NULL)
+ {
+ foreach ($this->cache as $key => $data)
+ $this->_clear_cache_record('IMAP.'.$key);
+
+ $this->cache = array();
+ $this->cache_changed = FALSE;
+ $this->cache_changes = array();
+ }
+ else
+ {
+ $this->_clear_cache_record('IMAP.'.$key);
+ $this->cache_changes[$key] = FALSE;
+ unset($this->cache[$key]);
+ }
+ }
+
+
+
+ function _read_cache_record($key)
+ {
+ $cache_data = FALSE;
+
+ if ($this->db)
+ {
+ // get cached data from DB
+ $sql_result = $this->db->query(
+ "SELECT cache_id, data
+ FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $key);
+
+ if ($sql_arr = $this->db->fetch_assoc($sql_result))
+ {
+ $cache_data = $sql_arr['data'];
+ $this->cache_keys[$key] = $sql_arr['cache_id'];
+ }
+ }
+
+ return $cache_data;
+ }
+
+
+ function _write_cache_record($key, $data)
+ {
+ if (!$this->db)
+ return FALSE;
+
+ // check if we already have a cache entry for this key
+ if (!isset($this->cache_keys[$key]))
+ {
+ $sql_result = $this->db->query(
+ "SELECT cache_id
+ FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $key);
+
+ if ($sql_arr = $this->db->fetch_assoc($sql_result))
+ $this->cache_keys[$key] = $sql_arr['cache_id'];
+ else
+ $this->cache_keys[$key] = FALSE;
+ }
+
+ // update existing cache record
+ if ($this->cache_keys[$key])
+ {
+ $this->db->query(
+ "UPDATE ".get_table_name('cache')."
+ SET created=now(),
+ data=?
+ WHERE user_id=?
+ AND cache_key=?",
+ $data,
+ $_SESSION['user_id'],
+ $key);
+ }
+ // add new cache record
+ else
+ {
+ $this->db->query(
+ "INSERT INTO ".get_table_name('cache')."
+ (created, user_id, cache_key, data)
+ VALUES (now(), ?, ?, ?)",
+ $_SESSION['user_id'],
+ $key,
+ $data);
+ }
+ }
+
+
+ function _clear_cache_record($key)
+ {
+ $this->db->query(
+ "DELETE FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $key);
+ }
+
+
+
+ /* --------------------------------
+ * message caching methods
+ * --------------------------------*/
+
+
+ // checks if the cache is up-to-date
+ // return: -3 = off, -2 = incomplete, -1 = dirty
+ function check_cache_status($mailbox, $cache_key)
+ {
+ if (!$this->caching_enabled)
+ return -3;
+
+ $cache_index = $this->get_message_cache_index($cache_key, TRUE);
+ $msg_count = $this->_messagecount($mailbox);
+ $cache_count = count($cache_index);
+
+ // console("Cache check: $msg_count !== ".count($cache_index));
+
+ if ($cache_count==$msg_count)
+ {
+ // get highest index
+ $header = iil_C_FetchHeader($this->conn, $mailbox, "$msg_count");
+ $cache_uid = array_pop($cache_index);
+
+ // uids of highest message matches -> cache seems OK
+ if ($cache_uid == $header->uid)
+ return 1;
+
+ // cache is dirty
+ return -1;
+ }
+ // if cache count differs less than 10% report as dirty
+ else if (abs($msg_count - $cache_count) < $msg_count/10)
+ return -1;
+ else
+ return -2;
+ }
+
+
+
+ function get_message_cache($key, $from, $to, $sort_field, $sort_order)
+ {
+ $cache_key = "$key:$from:$to:$sort_field:$sort_order";
+ $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size');
+
+ if (!in_array($sort_field, $db_header_fields))
+ $sort_field = 'idx';
+
+ if ($this->caching_enabled && !isset($this->cache[$cache_key]))
+ {
+ $this->cache[$cache_key] = array();
+ $sql_result = $this->db->limitquery(
+ "SELECT idx, uid, headers
+ FROM ".get_table_name('messages')."
+ WHERE user_id=?
+ AND cache_key=?
+ ORDER BY ".$this->db->quoteIdentifier($sort_field)." ".
+ strtoupper($sort_order),
+ $from,
+ $to-$from,
+ $_SESSION['user_id'],
+ $key);
+
+ while ($sql_arr = $this->db->fetch_assoc($sql_result))
+ {
+ $uid = $sql_arr['uid'];
+ $this->cache[$cache_key][$uid] = unserialize($sql_arr['headers']);
+ }
+ }
+
+ return $this->cache[$cache_key];
+ }
+
+
+ function get_cached_message($key, $uid, $body=FALSE)
+ {
+ if (!$this->caching_enabled)
+ return FALSE;
+
+ $internal_key = '__single_msg';
+ if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) || $body))
+ {
+ $sql_select = "idx, uid, headers";
+ if ($body)
+ $sql_select .= ", body";
+
+ $sql_result = $this->db->query(
+ "SELECT $sql_select
+ FROM ".get_table_name('messages')."
+ WHERE user_id=?
+ AND cache_key=?
+ AND uid=?",
+ $_SESSION['user_id'],
+ $key,
+ $uid);
+
+ if ($sql_arr = $this->db->fetch_assoc($sql_result))
+ {
+ $headers = unserialize($sql_arr['headers']);
+ if (is_object($headers) && !empty($sql_arr['body']))
+ $headers->body = $sql_arr['body'];
+
+ $this->cache[$internal_key][$uid] = $headers;
+ }
+ }
+
+ return $this->cache[$internal_key][$uid];
+ }
+
+
+ function get_message_cache_index($key, $force=FALSE, $sort_col='idx', $sort_order='ASC')
+ {
+ static $sa_message_index = array();
+
+ // empty key -> empty array
+ if (empty($key))
+ return array();
+
+ if (!empty($sa_message_index[$key]) && !$force)
+ return $sa_message_index[$key];
+
+ $sa_message_index[$key] = array();
+ $sql_result = $this->db->query(
+ "SELECT idx, uid
+ FROM ".get_table_name('messages')."
+ WHERE user_id=?
+ AND cache_key=?
+ ORDER BY ".$this->db->quote_identifier($sort_col)." ".$sort_order,
+ $_SESSION['user_id'],
+ $key);
+
+ while ($sql_arr = $this->db->fetch_assoc($sql_result))
+ $sa_message_index[$key][$sql_arr['idx']] = $sql_arr['uid'];
+
+ return $sa_message_index[$key];
+ }
+
+
+ function add_message_cache($key, $index, $headers)
+ {
+ if (!$key || !is_object($headers) || empty($headers->uid))
+ return;
+
+ $this->db->query(
+ "INSERT INTO ".get_table_name('messages')."
+ (user_id, del, cache_key, created, idx, uid, subject, ".$this->db->quoteIdentifier('from').", ".$this->db->quoteIdentifier('to').", cc, date, size, headers)
+ VALUES (?, 0, ?, now(), ?, ?, ?, ?, ?, ?, ".$this->db->fromunixtime($headers->timestamp).", ?, ?)",
+ $_SESSION['user_id'],
+ $key,
+ $index,
+ $headers->uid,
+ (string)substr($this->decode_header($headers->subject, TRUE), 0, 128),
+ (string)substr($this->decode_header($headers->from, TRUE), 0, 128),
+ (string)substr($this->decode_header($headers->to, TRUE), 0, 128),
+ (string)substr($this->decode_header($headers->cc, TRUE), 0, 128),
+ (int)$headers->size,
+ serialize($headers));
+ }
+
+
+ function remove_message_cache($key, $index)
+ {
+ $this->db->query(
+ "DELETE FROM ".get_table_name('messages')."
+ WHERE user_id=?
+ AND cache_key=?
+ AND idx=?",
+ $_SESSION['user_id'],
+ $key,
+ $index);
+ }
+
+
+ function clear_message_cache($key, $start_index=1)
+ {
+ $this->db->query(
+ "DELETE FROM ".get_table_name('messages')."
+ WHERE user_id=?
+ AND cache_key=?
+ AND idx>=?",
+ $_SESSION['user_id'],
+ $key,
+ $start_index);
+ }
+
+
+
+
+ /* --------------------------------
+ * encoding/decoding methods
+ * --------------------------------*/
+
+
+ function decode_address_list($input, $max=NULL)
+ {
+ $a = $this->_parse_address_list($input);
+ $out = array();
+
+ if (!is_array($a))
+ return $out;
+
+ $c = count($a);
+ $j = 0;
+
+ foreach ($a as $val)
+ {
+ $j++;
+ $address = $val['address'];
+ $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name']));
+ $string = $name!==$address ? sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address) : $address;
+
+ $out[$j] = array('name' => $name,
+ 'mailto' => $address,
+ 'string' => $string);
+
+ if ($max && $j==$max)
+ break;
+ }
+
+ return $out;
+ }
+
+
+ function decode_header($input, $remove_quotes=FALSE)
+ {
+ $str = $this->decode_mime_string((string)$input);
+ if ($str{0}=='"' && $remove_quotes)
+ {
+ $str = str_replace('"', '', $str);
+ }
+
+ return $str;
+ }
+
+
+ /**
+ * Decode a mime-encoded string to internal charset
+ *
+ * @access static
+ */
+ function decode_mime_string($input, $recursive=false)
+ {
+ $out = '';
+
+ $pos = strpos($input, '=?');
+ if ($pos !== false)
+ {
+ $out = substr($input, 0, $pos);
+
+ $end_cs_pos = strpos($input, "?", $pos+2);
+ $end_en_pos = strpos($input, "?", $end_cs_pos+1);
+ $end_pos = strpos($input, "?=", $end_en_pos+1);
+
+ $encstr = substr($input, $pos+2, ($end_pos-$pos-2));
+ $rest = substr($input, $end_pos+2);
+
+ $out .= rcube_imap::_decode_mime_string_part($encstr);
+ $out .= rcube_imap::decode_mime_string($rest);
+
+ return $out;
+ }
+
+ // no encoding information, defaults to what is specified in the class header
+ return rcube_charset_convert($input, 'ISO-8859-1');
+ }
+
+
+ /**
+ * Decode a part of a mime-encoded string
+ *
+ * @access static
+ */
+ function _decode_mime_string_part($str)
+ {
+ $a = explode('?', $str);
+ $count = count($a);
+
+ // should be in format "charset?encoding?base64_string"
+ if ($count >= 3)
+ {
+ for ($i=2; $i<$count; $i++)
+ $rest.=$a[$i];
+
+ if (($a[1]=="B")||($a[1]=="b"))
+ $rest = base64_decode($rest);
+ else if (($a[1]=="Q")||($a[1]=="q"))
+ {
+ $rest = str_replace("_", " ", $rest);
+ $rest = quoted_printable_decode($rest);
+ }
+
+ return rcube_charset_convert($rest, $a[0]);
+ }
+ else
+ return $str; // we dont' know what to do with this
+ }
+
+
+ function mime_decode($input, $encoding='7bit')
+ {
+ switch (strtolower($encoding))
+ {
+ case '7bit':
+ return $input;
+ break;
+
+ case 'quoted-printable':
+ return quoted_printable_decode($input);
+ break;
+
+ case 'base64':
+ return base64_decode($input);
+ break;
+
+ default:
+ return $input;
+ }
+ }
+
+
+ function mime_encode($input, $encoding='7bit')
+ {
+ switch ($encoding)
+ {
+ case 'quoted-printable':
+ return quoted_printable_encode($input);
+ break;
+
+ case 'base64':
+ return base64_encode($input);
+ break;
+
+ default:
+ return $input;
+ }
+ }
+
+
+ // convert body chars according to the ctype_parameters
+ function charset_decode($body, $ctype_param)
+ {
+ if (is_array($ctype_param) && !empty($ctype_param['charset']))
+ return rcube_charset_convert($body, $ctype_param['charset']);
+
+ // defaults to what is specified in the class header
+ return rcube_charset_convert($body, 'ISO-8859-1');
+ }
+
+
+
+
+ /* --------------------------------
+ * private methods
+ * --------------------------------*/
+
+
+ function _mod_mailbox($mbox_name, $mode='in')
+ {
+ if ((!empty($this->root_ns) && $this->root_ns == $mbox_name) || $mbox_name == 'INBOX')
+ return $mbox_name;
+
+ if (!empty($this->root_dir) && $mode=='in')
+ $mbox_name = $this->root_dir.$this->delimiter.$mbox_name;
+ else if (strlen($this->root_dir) && $mode=='out')
+ $mbox_name = substr($mbox_name, strlen($this->root_dir)+1);
+
+ return $mbox_name;
+ }
+
+
+ // sort mailboxes first by default folders and then in alphabethical order
+ function _sort_mailbox_list($a_folders)
+ {
+ $a_out = $a_defaults = array();
+
+ // find default folders and skip folders starting with '.'
+ foreach($a_folders as $i => $folder)
+ {
+ if ($folder{0}=='.')
+ continue;
+
+ if (($p = array_search(strtolower($folder), $this->default_folders_lc))!==FALSE)
+ $a_defaults[$p] = $folder;
+ else
+ $a_out[] = $folder;
+ }
+
+ sort($a_out);
+ ksort($a_defaults);
+
+ return array_merge($a_defaults, $a_out);
+ }
+
+ function get_id($uid, $mbox_name=NULL)
+ {
+ return $this->_uid2id($uid, $mbox_name);
+ }
+
+ function get_uid($id,$mbox_name=NULL)
+ {
+ return $this->_id2uid($id, $mbox_name);
+ }
+
+ function _uid2id($uid, $mbox_name=NULL)
+ {
+ if (!$mbox_name)
+ $mbox_name = $this->mailbox;
+
+ if (!isset($this->uid_id_map[$mbox_name][$uid]))
+ $this->uid_id_map[$mbox_name][$uid] = iil_C_UID2ID($this->conn, $mbox_name, $uid);
+
+ return $this->uid_id_map[$mbox_name][$uid];
+ }
+
+ function _id2uid($id, $mbox_name=NULL)
+ {
+ if (!$mbox_name)
+ $mbox_name = $this->mailbox;
+
+ return iil_C_ID2UID($this->conn, $mbox_name, $id);
+ }
+
+
+ // parse string or array of server capabilities and put them in internal array
+ function _parse_capability($caps)
+ {
+ if (!is_array($caps))
+ $cap_arr = explode(' ', $caps);
+ else
+ $cap_arr = $caps;
+
+ foreach ($cap_arr as $cap)
+ {
+ if ($cap=='CAPABILITY')
+ continue;
+
+ if (strpos($cap, '=')>0)
+ {
+ list($key, $value) = explode('=', $cap);
+ if (!is_array($this->capabilities[$key]))
+ $this->capabilities[$key] = array();
+
+ $this->capabilities[$key][] = $value;
+ }
+ else
+ $this->capabilities[$cap] = TRUE;
+ }
+ }
+
+
+ // subscribe/unsubscribe a list of mailboxes and update local cache
+ function _change_subscription($a_mboxes, $mode)
+ {
+ $updated = FALSE;
+
+ if (is_array($a_mboxes))
+ foreach ($a_mboxes as $i => $mbox_name)
+ {
+ $mailbox = $this->_mod_mailbox($mbox_name);
+ $a_mboxes[$i] = $mailbox;
+
+ if ($mode=='subscribe')
+ $result = iil_C_Subscribe($this->conn, $mailbox);
+ else if ($mode=='unsubscribe')
+ $result = iil_C_UnSubscribe($this->conn, $mailbox);
+
+ if ($result>=0)
+ $updated = TRUE;
+ }
+
+ // get cached mailbox list
+ if ($updated)
+ {
+ $a_mailbox_cache = $this->get_cache('mailboxes');
+ if (!is_array($a_mailbox_cache))
+ return $updated;
+
+ // modify cached list
+ if ($mode=='subscribe')
+ $a_mailbox_cache = array_merge($a_mailbox_cache, $a_mboxes);
+ else if ($mode=='unsubscribe')
+ $a_mailbox_cache = array_diff($a_mailbox_cache, $a_mboxes);
+
+ // write mailboxlist to cache
+ $this->update_cache('mailboxes', $this->_sort_mailbox_list($a_mailbox_cache));
+ }
+
+ return $updated;
+ }
+
+
+ // increde/decrese messagecount for a specific mailbox
+ function _set_messagecount($mbox_name, $mode, $increment)
+ {
+ $a_mailbox_cache = FALSE;
+ $mailbox = $mbox_name ? $mbox_name : $this->mailbox;
+ $mode = strtoupper($mode);
+
+ $a_mailbox_cache = $this->get_cache('messagecount');
+
+ if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
+ return FALSE;
+
+ // add incremental value to messagecount
+ $a_mailbox_cache[$mailbox][$mode] += $increment;
+
+ // there's something wrong, delete from cache
+ if ($a_mailbox_cache[$mailbox][$mode] < 0)
+ unset($a_mailbox_cache[$mailbox][$mode]);
+
+ // write back to cache
+ $this->update_cache('messagecount', $a_mailbox_cache);
+
+ return TRUE;
+ }
+
+
+ // remove messagecount of a specific mailbox from cache
+ function _clear_messagecount($mbox_name='')
+ {
+ $a_mailbox_cache = FALSE;
+ $mailbox = $mbox_name ? $mbox_name : $this->mailbox;
+
+ $a_mailbox_cache = $this->get_cache('messagecount');
+
+ if (is_array($a_mailbox_cache[$mailbox]))
+ {
+ unset($a_mailbox_cache[$mailbox]);
+ $this->update_cache('messagecount', $a_mailbox_cache);
+ }
+ }
+
+
+ function _parse_address_list($str)
+ {
+ $a = $this->_explode_quoted_string(',', $str);
+ $result = array();
+
+ foreach ($a as $key => $val)
+ {
+ $val = str_replace("\"<", "\" <", $val);
+ $sub_a = $this->_explode_quoted_string(' ', $this->decode_header($val));
+ $result[$key]['name'] = '';
+
+ foreach ($sub_a as $k => $v)
+ {
+ if ((strpos($v, '@') > 0) && (strpos($v, '.') > 0))
+ $result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v));
+ else
+ $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
+ }
+
+ if (empty($result[$key]['name']))
+ $result[$key]['name'] = $result[$key]['address'];
+ }
+
+ return $result;
+ }
+
+
+ function _explode_quoted_string($delimiter, $string)
+ {
+ $quotes = explode("\"", $string);
+ foreach ($quotes as $key => $val)
+ if (($key % 2) == 1)
+ $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
+
+ $string = implode("\"", $quotes);
+
+ $result = explode($delimiter, $string);
+ foreach ($result as $key => $val)
+ $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]);
+
+ return $result;
+ }
+ }
+
+
+
+/**
+ * rcube_header_sorter
+ *
+ * Class for sorting an array of iilBasicHeader objects in a predetermined order.
+ *
+ * @author Eric Stadtherr
+ */
+class rcube_header_sorter
+{
+ var $sequence_numbers = array();
+
+ /**
+ * set the predetermined sort order.
+ *
+ * @param array $seqnums numerically indexed array of IMAP message sequence numbers
+ */
+ function set_sequence_numbers($seqnums)
+ {
+ $this->sequence_numbers = $seqnums;
+ }
+
+ /**
+ * sort the array of header objects
+ *
+ * @param array $headers array of iilBasicHeader objects indexed by UID
+ */
+ function sort_headers(&$headers)
+ {
+ /*
+ * uksort would work if the keys were the sequence number, but unfortunately
+ * the keys are the UIDs. We'll use uasort instead and dereference the value
+ * to get the sequence number (in the "id" field).
+ *
+ * uksort($headers, array($this, "compare_seqnums"));
+ */
+ uasort($headers, array($this, "compare_seqnums"));
+ }
+
+ /**
+ * get the position of a message sequence number in my sequence_numbers array
+ *
+ * @param integer $seqnum message sequence number contained in sequence_numbers
+ */
+ function position_of($seqnum)
+ {
+ $c = count($this->sequence_numbers);
+ for ($pos = 0; $pos <= $c; $pos++)
+ {
+ if ($this->sequence_numbers[$pos] == $seqnum)
+ return $pos;
+ }
+ return -1;
+ }
+
+ /**
+ * Sort method called by uasort()
+ */
+ function compare_seqnums($a, $b)
+ {
+ // First get the sequence number from the header object (the 'id' field).
+ $seqa = $a->id;
+ $seqb = $b->id;
+
+ // then find each sequence number in my ordered list
+ $posa = $this->position_of($seqa);
+ $posb = $this->position_of($seqb);
+
+ // return the relative position as the comparison value
+ $ret = $posa - $posb;
+ return $ret;
+ }
+}
+
+
+/**
+ * Add quoted-printable encoding to a given string
+ *
+ * @param string $input string to encode
+ * @param int $line_max add new line after this number of characters
+ * @param boolena $space_conf true if spaces should be converted into =20
+ * @return encoded string
+ */
+function quoted_printable_encode($input, $line_max=76, $space_conv=false)
+ {
+ $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
+ $lines = preg_split("/(?:\r\n|\r|\n)/", $input);
+ $eol = "\r\n";
+ $escape = "=";
+ $output = "";
+
+ while( list(, $line) = each($lines))
+ {
+ //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
+ $linlen = strlen($line);
+ $newline = "";
+ for($i = 0; $i < $linlen; $i++)
+ {
+ $c = substr( $line, $i, 1 );
+ $dec = ord( $c );
+ if ( ( $i == 0 ) && ( $dec == 46 ) ) // convert first point in the line into =2E
+ {
+ $c = "=2E";
+ }
+ if ( $dec == 32 )
+ {
+ if ( $i == ( $linlen - 1 ) ) // convert space at eol only
+ {
+ $c = "=20";
+ }
+ else if ( $space_conv )
+ {
+ $c = "=20";
+ }
+ }
+ else if ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) // always encode "\t", which is *not* required
+ {
+ $h2 = floor($dec/16);
+ $h1 = floor($dec%16);
+ $c = $escape.$hex["$h2"].$hex["$h1"];
+ }
+
+ if ( (strlen($newline) + strlen($c)) >= $line_max ) // CRLF is not counted
+ {
+ $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
+ $newline = "";
+ // check if newline first character will be point or not
+ if ( $dec == 46 )
+ {
+ $c = "=2E";
+ }
+ }
+ $newline .= $c;
+ } // end of for
+ $output .= $newline.$eol;
+ } // end of while
+
+ return trim($output);
+ }
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_ldap.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Manage an LDAP connection |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Jeremy Jongsma <jeremy@jongsma.org> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_ldap.inc 95 2006-01-08 07:15:44Z justinrandell $
+
+*/
+
+require_once("bugs.inc");
+
+class rcube_ldap
+ {
+ var $conn;
+ var $host;
+ var $port;
+ var $protocol;
+ var $base_dn;
+ var $bind_dn;
+ var $bind_pass;
+
+ // PHP 5 constructor
+ function __construct()
+ {
+ }
+
+ // PHP 4 constructor
+ function rcube_ldap()
+ {
+ $this->__construct();
+ }
+
+ function connect($hosts, $port=389, $protocol=3)
+ {
+ if (!function_exists('ldap_connect'))
+ raise_error(array("type" => "ldap",
+ "message" => "No ldap support in this installation of php."),
+ TRUE);
+
+ if (is_resource($this->conn))
+ return TRUE;
+
+ if (!is_array($hosts))
+ $hosts = array($hosts);
+
+ foreach ($hosts as $host)
+ {
+ if ($lc = @ldap_connect($host, $port))
+ {
+ @ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $protocol);
+ $this->host = $host;
+ $this->port = $port;
+ $this->protocol = $protocol;
+ $this->conn = $lc;
+ return TRUE;
+ }
+ }
+
+ if (!is_resource($this->conn))
+ raise_error(array("type" => "ldap",
+ "message" => "Could not connect to any LDAP server, tried $host:$port last"),
+ TRUE);
+ }
+
+ function close()
+ {
+ if ($this->conn)
+ {
+ if (@ldap_unbind($this->conn))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Could not close connection to LDAP server: ".ldap_error($this->conn)),
+ TRUE);
+ }
+ return FALSE;
+ }
+
+ // Merge with connect()?
+ function bind($dn=null, $pass=null)
+ {
+ if ($this->conn)
+ {
+ if ($dn)
+ if (@ldap_bind($this->conn, $dn, $pass))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Bind failed for dn=$dn: ".ldap_error($this->conn)),
+ TRUE);
+ else
+ if (@ldap_bind($this->conn))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Anonymous bind failed: ".ldap_error($this->conn)),
+ TRUE);
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Attempted bind on nonexistent connection"), TRUE);
+ return FALSE;
+ }
+
+ function count($base, $filter=null, $attributes=null, $scope="sub")
+ {
+ if ($this->conn)
+ {
+ if ($scope === 'sub')
+ $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit);
+ else if ($scope === 'one')
+ $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit);
+ else if ($scope === 'base')
+ $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit);
+ if ($sr)
+ return @ldap_count_entries($this->conn, $sr);
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Attempted count search on nonexistent connection"), TRUE);
+ return FALSE;
+ }
+
+ function search($base, $filter=null, $attributes=null, $scope='sub', $sort=null, $limit=0)
+ {
+ if ($this->conn)
+ {
+ if ($scope === 'sub')
+ $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit);
+ else if ($scope === 'one')
+ $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit);
+ else if ($scope === 'base')
+ $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit);
+ if ($sr)
+ {
+ if ($sort && $scope !== "base")
+ {
+ if (is_array($sort))
+ {
+ // Start from the end so first sort field has highest priority
+ $sortfields = array_reverse($sort);
+ foreach ($sortfields as $sortfield)
+ @ldap_sort($this->conn, $sr, $sortfield);
+ }
+ else
+ @ldap_sort($this->conn, $sr, $sort);
+ }
+ return @ldap_get_entries($this->conn, $sr);
+ }
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Attempted search on nonexistent connection"), TRUE);
+ return FALSE;
+ }
+
+ function add($dn, $object)
+ {
+ if ($this->conn)
+ {
+ if (@ldap_add($this->conn, $dn, $object))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Add object failed: ".ldap_error($this->conn)),
+ TRUE);
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Add object faile: no connection"),
+ TRUE);
+ return FALSE;
+ }
+
+ function modify($dn, $object)
+ {
+ if ($this->conn)
+ {
+ if (@ldap_modify($this->conn, $dn, $object))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Modify object failed: ".ldap_error($this->conn)),
+ TRUE);
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Modify object failed: no connection"),
+ TRUE);
+ return FALSE;
+ }
+
+ function rename($dn, $newrdn, $parentdn)
+ {
+ if ($this->protocol < 3)
+ {
+ raise_error(array("type" => "ldap",
+ "message" => "rename() support requires LDAPv3 or above "),
+ TRUE);
+ return FALSE;
+ }
+
+ if ($this->conn)
+ {
+ if (@ldap_rename($this->conn, $dn, $newrdn, $parentdn, TRUE))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Rename object failed: ".ldap_error($this->conn)),
+ TRUE);
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Rename object failed: no connection"),
+ TRUE);
+ return FALSE;
+ }
+
+ function delete($dn)
+ {
+ if ($this->conn)
+ {
+ if (@ldap_delete($this->conn, $dn))
+ return TRUE;
+ else
+ raise_error(array("code" => ldap_errno($this->conn),
+ "type" => "ldap",
+ "message" => "Delete object failed: ".ldap_error($this->conn)),
+ TRUE);
+ }
+ else
+ raise_error(array("type" => "ldap",
+ "message" => "Delete object failed: no connection"),
+ TRUE);
+ return FALSE;
+ }
+
+ }
+
+// vi: et ts=2 sw=2
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_mdb2.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | PEAR:DB wrapper class that implements PEAR MDB2 functions |
+ | See http://pear.php.net/package/MDB2 |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Lukas Kahwe Smith <smith@pooteeweet.org> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_mdb2.inc 239 2006-05-18 21:25:11Z roundcube $
+
+*/
+
+
+/**
+ * Obtain the PEAR::DB class that is used for abstraction
+ */
+require_once('MDB2.php');
+
+
+/**
+ * Database independent query interface
+ *
+ * This is a wrapper for the PEAR::MDB2 class
+ *
+ * @package RoundCube Webmail
+ * @author David Saez Padros <david@ols.es>
+ * @author Thomas Bruederli <roundcube@gmail.com>
+ * @author Lukas Kahwe Smith <smith@pooteeweet.org>
+ * @version 1.16
+ * @link http://pear.php.net/package/MDB2
+ */
+class rcube_db
+ {
+ var $db_dsnw; // DSN for write operations
+ var $db_dsnr; // DSN for read operations
+ var $db_connected = false; // Already connected ?
+ var $db_mode = ''; // Connection mode
+ var $db_handle = 0; // Connection handle
+ var $db_error = false;
+ var $db_error_msg = '';
+
+ var $a_query_results = array('dummy');
+ var $last_res_id = 0;
+
+
+ /**
+ * Object constructor
+ *
+ * @param string DSN for read/write operations
+ * @param string Optional DSN for read only operations
+ */
+ function __construct($db_dsnw, $db_dsnr='')
+ {
+ if ($db_dsnr=='')
+ $db_dsnr=$db_dsnw;
+
+ $this->db_dsnw = $db_dsnw;
+ $this->db_dsnr = $db_dsnr;
+
+ $dsn_array = MDB2::parseDSN($db_dsnw);
+ $this->db_provider = $dsn_array['phptype'];
+ }
+
+
+ /**
+ * PHP 4 object constructor
+ *
+ * @see rcube_MDB2::__construct
+ */
+ function rcube_db($db_dsnw,$db_dsnr='')
+ {
+ $this->__construct($db_dsnw,$db_dsnr);
+ }
+
+
+ /**
+ * Connect to specific database
+ *
+ * @param string DSN for DB connections
+ * @return object PEAR database handle
+ * @access private
+ */
+ function dsn_connect($dsn)
+ {
+ // Use persistent connections if available
+ $dbh = MDB2::connect($dsn, array('persistent' => TRUE, 'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL));
+
+ if (PEAR::isError($dbh))
+ {
+ $this->db_error = TRUE;
+ $this->db_error_msg = $dbh->getMessage();
+
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $dbh->getMessage()), TRUE, FALSE);
+ }
+
+ else if ($this->db_provider=='sqlite')
+ {
+ $dsn_array = MDB2::parseDSN($dsn);
+ if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials))
+ $this->_sqlite_create_database($dbh, $this->sqlite_initials);
+ }
+
+ return $dbh;
+ }
+
+
+ /**
+ * Connect to appropiate databse
+ * depending on the operation
+ *
+ * @param string Connection mode (r|w)
+ * @access public
+ */
+ function db_connect($mode)
+ {
+ $this->db_mode = $mode;
+
+ // Already connected
+ if ($this->db_connected)
+ {
+ // no replication, current connection is ok
+ if ($this->db_dsnw==$this->db_dsnr)
+ return;
+
+ // connected to master, current connection is ok
+ if ($this->db_mode=='w')
+ return;
+
+ // Same mode, current connection is ok
+ if ($this->db_mode==$mode)
+ return;
+ }
+
+ if ($mode=='r')
+ $dsn = $this->db_dsnr;
+ else
+ $dsn = $this->db_dsnw;
+
+ $this->db_handle = $this->dsn_connect($dsn);
+ $this->db_connected = true;
+ }
+
+
+
+ /**
+ * Getter for error state
+ *
+ * @param boolean True on error
+ */
+ function is_error()
+ {
+ return $this->db_error ? $this->db_error_msg : FALSE;
+ }
+
+
+ /**
+ * Execute a SQL query
+ *
+ * @param string SQL query to execute
+ * @param mixed Values to be inserted in query
+ * @return number Query handle identifier
+ * @access public
+ */
+ function query()
+ {
+ $params = func_get_args();
+ $query = array_shift($params);
+
+ return $this->_query($query, 0, 0, $params);
+ }
+
+
+ /**
+ * Execute a SQL query with limits
+ *
+ * @param string SQL query to execute
+ * @param number Offset for LIMIT statement
+ * @param number Number of rows for LIMIT statement
+ * @param mixed Values to be inserted in query
+ * @return number Query handle identifier
+ * @access public
+ */
+ function limitquery()
+ {
+ $params = func_get_args();
+ $query = array_shift($params);
+ $offset = array_shift($params);
+ $numrows = array_shift($params);
+
+ return $this->_query($query, $offset, $numrows, $params);
+ }
+
+
+ /**
+ * Execute a SQL query with limits
+ *
+ * @param string SQL query to execute
+ * @param number Offset for LIMIT statement
+ * @param number Number of rows for LIMIT statement
+ * @param array Values to be inserted in query
+ * @return number Query handle identifier
+ * @access private
+ */
+ function _query($query, $offset, $numrows, $params)
+ {
+ // Read or write ?
+ if (strtolower(trim(substr($query,0,6)))=='select')
+ $mode='r';
+ else
+ $mode='w';
+
+ $this->db_connect($mode);
+
+ if ($this->db_provider == 'sqlite')
+ $this->_sqlite_prepare();
+
+ if ($numrows || $offset)
+ $result = $this->db_handle->setLimit($numrows,$offset);
+
+ if (empty($params))
+ $result = $this->db_handle->query($query);
+ else
+ {
+ $params = (array)$params;
+ $q = $this->db_handle->prepare($query);
+ if ($this->db_handle->isError($q))
+ {
+ $this->db_error = TRUE;
+ $this->db_error_msg = $q->userinfo;
+
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $this->db_error_msg), TRUE, TRUE);
+ }
+ else
+ {
+ $result = $q->execute($params);
+ $q->free();
+ }
+ }
+
+ // add result, even if it's an error
+ return $this->_add_result($result);
+ }
+
+
+ /**
+ * Get number of rows for a SQL query
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Number of rows or FALSE on failure
+ * @access public
+ */
+ function num_rows($res_id=NULL)
+ {
+ if (!$this->db_handle)
+ return FALSE;
+
+ if ($result = $this->_get_result($res_id))
+ return $result->numRows();
+ else
+ return FALSE;
+ }
+
+
+ /**
+ * Get number of affected rows fort he last query
+ *
+ * @return mixed Number of rows or FALSE on failure
+ * @access public
+ */
+ function affected_rows($result = null)
+ {
+ if (!$this->db_handle)
+ return FALSE;
+
+ return $result;
+ }
+
+
+ /**
+ * Get last inserted record ID
+ * For Postgres databases, a sequence name is required
+ *
+ * @param string Sequence name for increment
+ * @return mixed ID or FALSE on failure
+ * @access public
+ */
+ function insert_id($sequence = '')
+ {
+ if (!$this->db_handle || $this->db_mode=='r')
+ return FALSE;
+
+ return $this->db_handle->lastInsertID($sequence);
+ }
+
+
+ /**
+ * Get an associative array for one row
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access public
+ */
+ function fetch_assoc($res_id=NULL)
+ {
+ $result = $this->_get_result($res_id);
+ return $this->_fetch_row($result, MDB2_FETCHMODE_ASSOC);
+ }
+
+
+ /**
+ * Get an index array for one row
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access public
+ */
+ function fetch_array($res_id=NULL)
+ {
+ $result = $this->_get_result($res_id);
+ return $this->_fetch_row($result, MDB2_FETCHMODE_ORDERED);
+ }
+
+
+ /**
+ * Get co values for a result row
+ *
+ * @param object Query result handle
+ * @param number Fetch mode identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access private
+ */
+ function _fetch_row($result, $mode)
+ {
+ if (PEAR::isError($result))
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $this->db_link->getMessage()), TRUE, FALSE);
+ return FALSE;
+ }
+
+ return $result->fetchRow($mode);
+ }
+
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed Value to quote
+ * @return string Quoted/converted string for use in query
+ * @access public
+ */
+ function quote($input, $type = null)
+ {
+ // create DB handle if not available
+ if (!$this->db_handle)
+ $this->db_connect('r');
+
+ // escape pear identifier chars
+ $rep_chars = array('?' => '\?',
+ '!' => '\!',
+ '&' => '\&');
+
+ return $this->db_handle->quote($input, $type);
+ }
+
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string Value to quote
+ * @return string Quoted string for use in query
+ * @deprecated Replaced by rcube_MDB2::quote_identifier
+ * @see rcube_MDB2::quote_identifier
+ * @access public
+ */
+ function quoteIdentifier($str)
+ {
+ return $this->quote_identifier($str);
+ }
+
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string Value to quote
+ * @return string Quoted string for use in query
+ * @access public
+ */
+ function quote_identifier($str)
+ {
+ if (!$this->db_handle)
+ $this->db_connect('r');
+
+ return $this->db_handle->quoteIdentifier($str);
+ }
+
+
+ /**
+ * Return SQL statement to convert a field value into a unix timestamp
+ *
+ * @param string Field name
+ * @return string SQL statement to use in query
+ * @access public
+ */
+ function unixtimestamp($field)
+ {
+ switch($this->db_provider)
+ {
+ case 'pgsql':
+ return "EXTRACT (EPOCH FROM $field)";
+ break;
+
+ default:
+ return "UNIX_TIMESTAMP($field)";
+ }
+ }
+
+
+ /**
+ * Return SQL statement to convert from a unix timestamp
+ *
+ * @param string Field name
+ * @return string SQL statement to use in query
+ * @access public
+ */
+ function fromunixtime($timestamp)
+ {
+ switch($this->db_provider)
+ {
+ case 'mysqli':
+ case 'mysql':
+ case 'sqlite':
+ return "FROM_UNIXTIME($timestamp)";
+
+ default:
+ return date("'Y-m-d H:i:s'", $timestamp);
+ }
+ }
+
+
+ /**
+ * Adds a query result and returns a handle ID
+ *
+ * @param object Query handle
+ * @return mixed Handle ID or FALE on failure
+ * @access private
+ */
+ function _add_result($res)
+ {
+ // sql error occured
+ if (PEAR::isError($res))
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 512)), TRUE, FALSE);
+ return FALSE;
+ }
+ else
+ {
+ $res_id = sizeof($this->a_query_results);
+ $this->a_query_results[$res_id] = $res;
+ $this->last_res_id = $res_id;
+ return $res_id;
+ }
+ }
+
+
+ /**
+ * Resolves a given handle ID and returns the according query handle
+ * If no ID is specified, the last ressource handle will be returned
+ *
+ * @param number Handle ID
+ * @return mixed Ressource handle or FALE on failure
+ * @access private
+ */
+ function _get_result($res_id=NULL)
+ {
+ if ($res_id==NULL)
+ $res_id = $this->last_res_id;
+
+ if ($res_id && isset($this->a_query_results[$res_id]))
+ return $this->a_query_results[$res_id];
+ else
+ return FALSE;
+ }
+
+
+ /**
+ * Create a sqlite database from a file
+ *
+ * @param object SQLite database handle
+ * @param string File path to use for DB creation
+ * @access private
+ */
+ function _sqlite_create_database($dbh, $file_name)
+ {
+ if (empty($file_name) || !is_string($file_name))
+ return;
+
+ $data = '';
+ if ($fd = fopen($file_name, 'r'))
+ {
+ $data = fread($fd, filesize($file_name));
+ fclose($fd);
+ }
+
+ if (strlen($data))
+ sqlite_exec($dbh->connection, $data);
+ }
+
+
+ /**
+ * Add some proprietary database functions to the current SQLite handle
+ * in order to make it MySQL compatible
+ *
+ * @access private
+ */
+ function _sqlite_prepare()
+ {
+ include_once('include/rcube_sqlite.inc');
+
+ // we emulate via callback some missing MySQL function
+ sqlite_create_function($this->db_handle->connection, "from_unixtime", "rcube_sqlite_from_unixtime");
+ sqlite_create_function($this->db_handle->connection, "unix_timestamp", "rcube_sqlite_unix_timestamp");
+ sqlite_create_function($this->db_handle->connection, "now", "rcube_sqlite_now");
+ sqlite_create_function($this->db_handle->connection, "md5", "rcube_sqlite_md5");
+ }
+
+
+ } // end class rcube_db
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | rcube_shared.inc |
+ | |
+ | This file is part of the RoundCube PHP suite |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | CONTENTS: |
+ | Shared functions and classes used in PHP projects |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_shared.inc 288 2006-07-31 22:51:23Z thomasb $
+
+*/
+
+
+// ********* round cube schared classes *********
+
+class rcube_html_page
+ {
+ var $css;
+
+ var $scripts_path = '';
+ var $script_files = array();
+ var $scripts = array();
+ var $charset = 'ISO-8859-1';
+
+ var $script_tag_file = "<script type=\"text/javascript\" src=\"%s%s\"></script>\n";
+ var $script_tag = "<script type=\"text/javascript\">\n<!--\n%s\n\n//-->\n</script>\n";
+ var $default_template = "<html>\n<body></body>\n</html>";
+
+ var $title = '';
+ var $header = '';
+ var $footer = '';
+ var $body = '';
+ var $body_attrib = array();
+ var $meta_tags = array();
+
+
+ // PHP 5 constructor
+ function __construct()
+ {
+ $this->css = new rcube_css();
+ }
+
+ // PHP 4 compatibility
+ function rcube_html_page()
+ {
+ $this->__construct();
+ }
+
+
+ function include_script($file, $position='head')
+ {
+ static $sa_files = array();
+
+ if (in_array($file, $sa_files))
+ return;
+
+ if (!is_array($this->script_files[$position]))
+ $this->script_files[$position] = array();
+
+ $this->script_files[$position][] = $file;
+ }
+
+
+ function add_script($script, $position='head')
+ {
+ if (!isset($this->scripts[$position]))
+ $this->scripts[$position] = '';
+
+ $this->scripts[$position] .= "\n$script";
+ }
+
+
+ function set_title($t)
+ {
+ $this->title = $t;
+ }
+
+
+ function set_charset($charset)
+ {
+ global $MBSTRING;
+
+ $this->charset = $charset;
+
+ if ($MBSTRING && function_exists("mb_internal_encoding"))
+ {
+ if(!@mb_internal_encoding($charset))
+ $MBSTRING = FALSE;
+ }
+ }
+
+ function get_charset()
+ {
+ return $this->charset;
+ }
+
+
+ function reset()
+ {
+ $this->css = new rcube_css();
+ $this->script_files = array();
+ $this->scripts = array();
+ $this->title = '';
+ }
+
+
+ function write($templ='', $base_path='')
+ {
+ $output = empty($templ) ? $this->default_template : trim($templ);
+
+ // set default page title
+ if (!strlen($this->title))
+ $this->title = 'RoundCube Mail';
+
+ // replace specialchars in content
+ $__page_title = rep_specialchars_output($this->title, 'html', 'show', FALSE);
+ $__page_header = $__page_body = $__page_footer = '';
+
+
+ // include meta tag with charset
+ if (!empty($this->charset))
+ {
+ header('Content-Type: text/html; charset='.$this->charset);
+ $__page_header = '<meta http-equiv="content-type" content="text/html; charset='.$this->charset.'" />'."\n";
+ }
+
+
+ // definition of the code to be placed in the document header and footer
+ if (is_array($this->script_files['head']))
+ foreach ($this->script_files['head'] as $file)
+ $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file);
+
+ if (strlen($this->scripts['head']))
+ $__page_header .= sprintf($this->script_tag, $this->scripts['head']);
+
+ if (is_array($this->script_files['foot']))
+ foreach ($this->script_files['foot'] as $file)
+ $__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file);
+
+ if (strlen($this->scripts['foot']))
+ $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']);
+
+
+ $__page_header .= $this->css->show();
+
+
+ // find page header
+ if($hpos = strpos(strtolower($output), '</head>'))
+ $__page_header .= "\n";
+ else
+ {
+ if (!is_numeric($hpos))
+ $hpos = strpos(strtolower($output), '<body');
+ if (!is_numeric($hpos) && ($hpos = strpos(strtolower($output), '<html')))
+ {
+ while($output[$hpos]!='>')
+ $hpos++;
+ $hpos++;
+ }
+
+ $__page_header = "<head>\n<title>$__page_title</title>\n$__page_header\n</head>\n";
+ }
+
+ // add page hader
+ if($hpos)
+ $output = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output));
+ else
+ $output = $__page_header . $output;
+
+
+ // find page body
+ if($bpos = strpos(strtolower($output), '<body'))
+ {
+ while($output[$bpos]!='>') $bpos++;
+ $bpos++;
+ }
+ else
+ $bpos = strpos(strtolower($output), '</head>')+7;
+
+ // add page body
+ if($bpos && $__page_body)
+ $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output));
+
+
+ // find and add page footer
+ if(($fpos = strpos(strtolower($output), '</body>')) || ($fpos = strpos(strtolower($output), '</html>')))
+ $output = substr($output,0,$fpos) . "$__page_footer\n" . substr($output,$fpos,strlen($output));
+ else
+ $output .= "\n$__page_footer";
+
+
+ // reset those global vars
+ $__page_header = $__page_footer = '';
+
+
+ // correct absolute pathes in images and other tags
+ $output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output);
+ $output = str_replace('$__skin_path', $base_path, $output);
+
+ print rcube_charset_convert($output, 'UTF-8', $this->charset);
+ }
+
+
+ function _parse($templ)
+ {
+
+ }
+ }
+
+
+
+
+class rcube_css
+ {
+ var $css_data = array();
+
+ var $css_groups = array();
+
+ var $include_files = array();
+
+ var $grouped_output = TRUE;
+
+ var $content_type = 'text/css';
+
+ var $base_path = '';
+
+ var $indent_chars = "\t";
+
+
+ // add or overwrite a css definition
+ // either pass porperty and value as separate arguments
+ // or provide an associative array as second argument
+ function set_style($selector, $property, $value='')
+ {
+ $a_elements = $this->_parse_selectors($selector);
+ foreach ($a_elements as $element)
+ {
+ if (!is_array($property))
+ $property = array($property => $value);
+
+ foreach ($property as $name => $value)
+ $this->css_data[$element][strtolower($name)] = $value;
+ }
+
+ // clear goups array
+ $this->css_groups = array();
+ }
+
+
+ // unset a style property
+ function remove_style($selector, $property)
+ {
+ if (!is_array($property))
+ $property = array($property);
+
+ foreach ($property as $key)
+ unset($this->css_data[$selector][strtolower($key)]);
+
+ // clear goups array
+ $this->css_groups = array();
+ }
+
+
+ // define base path for external css files
+ function set_basepath($path)
+ {
+ $this->base_path = preg_replace('/\/$/', '', $path);
+ }
+
+
+ // enable/disable grouped output
+ function set_grouped_output($grouped)
+ {
+ $this->grouped_output = $grouped;
+ }
+
+
+ // add a css file as external source
+ function include_file($filename, $media='')
+ {
+ // include multiple files
+ if (is_array($filename))
+ {
+ foreach ($filename as $file)
+ $this->include_file($file, $media);
+ }
+ // add single file
+ else if (!in_array($filename, $this->include_files))
+ $this->include_files[] = array('file' => $filename,
+ 'media' => $media);
+ }
+
+
+ // parse css code
+ function import_string($str)
+ {
+ $ret = FALSE;
+ if (strlen($str))
+ $ret = $this->_parse($str);
+
+ return $ret;
+ }
+
+
+ // open and parse a css file
+ function import_file($file)
+ {
+ $ret = FALSE;
+
+ if (!is_file($file))
+ return $ret;
+
+ // for php version >= 4.3.0
+ if (function_exists('file_get_contents'))
+ $ret = $this->_parse(file_get_contents($file));
+
+ // for order php versions
+ else if ($fp = fopen($file, 'r'))
+ {
+ $ret = $this->_parse(fread($fp, filesize($file)));
+ fclose($fp);
+ }
+
+ return $ret;
+ }
+
+
+ // copy all properties inherited from superior styles to a specific selector
+ function copy_inherited_styles($selector)
+ {
+ // get inherited props from body and tag/class selectors
+ $css_props = $this->_get_inherited_styles($selector);
+
+ // write modified props back and clear goups array
+ if (sizeof($css_props))
+ {
+ $this->css_data[$selector] = $css_props;
+ $this->css_groups = array();
+ }
+ }
+
+
+ // return css definition for embedding in HTML
+ function show()
+ {
+ $out = '';
+
+ // include external css files
+ if (sizeof($this->include_files))
+ foreach ($this->include_files as $file_arr)
+ $out .= sprintf('<link rel="stylesheet" type="%s" href="%s"%s>'."\n",
+ $this->content_type,
+ $this->_get_file_path($file_arr['file']),
+ $file_arr['media'] ? ' media="'.$file_arr['media'].'"' : '');
+
+
+ // compose css string
+ if (sizeof($this->css_data))
+ $out .= sprintf("<style type=\"%s\">\n<!--\n\n%s-->\n</style>",
+ $this->content_type,
+ $this->to_string());
+
+
+ return $out;
+ }
+
+
+ // return valid css code of the current styles grid
+ function to_string($selector=NULL)
+ {
+ // return code for a single selector
+ if ($selector)
+ {
+ $indent_str = $this->indent_chars;
+ $this->indent_chars = '';
+
+ $prop_arr = $this->to_array($selector);
+ $out = $this->_style2string($prop_arr, TRUE);
+
+ $this->indent_chars = $indent_str;
+ }
+
+ // compose css code for complete data grid
+ else
+ {
+ $out = '';
+ $css_data = $this->to_array();
+
+ foreach ($css_data as $key => $prop_arr)
+ $out .= sprintf("%s {\n%s}\n\n",
+ $key,
+ $this->_style2string($prop_arr, TRUE));
+ }
+
+ return $out;
+ }
+
+
+ // return a single-line string of a css definition
+ function to_inline($selector)
+ {
+ if ($this->css_data[$selector])
+ return str_replace('"', '\\"', $this->_style2string($this->css_data[$selector], FALSE));
+ }
+
+
+ // return an associative array with selector(s) as key and styles array as value
+ function to_array($selector=NULL)
+ {
+ if (!$selector && $this->grouped_output)
+ {
+ // build groups if desired
+ if (!sizeof($this->css_groups))
+ $this->_build_groups();
+
+ // modify group array to get an array(selector => properties)
+ $out_arr = array();
+ foreach ($this->css_groups as $group_arr)
+ {
+ $key = join(', ', $group_arr['selectors']);
+ $out_arr[$key] = $group_arr['properties'];
+ }
+ }
+ else
+ $out_arr = $this->css_data;
+
+ return $selector ? $out_arr[$selector] : $out_arr;
+ }
+
+
+ // create a css file
+ function to_file($filepath)
+ {
+ if ($fp = fopen($filepath, 'w'))
+ {
+ fwrite($fp, $this->to_string());
+ fclose($fp);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ // alias method for import_string() [DEPRECATED]
+ function add($str)
+ {
+ $this->import_string($str);
+ }
+
+ // alias method for to_string() [DEPRECATED]
+ function get()
+ {
+ return $this->to_string();
+ }
+
+
+
+ // ******** private methods ********
+
+
+ // parse a string and add styles to internal data grid
+ function _parse($str)
+ {
+ // remove comments
+ $str = preg_replace("/\/\*(.*)?\*\//Usi", '', $str);
+
+ // parse style definitions
+ if (!preg_match_all ('/([a-z0-9\.#*:_][a-z0-9\.\-_#:*,\[\]\(\)\s\"\'\+\|>~=]+)\s*\{([^\}]*)\}/ims', $str, $matches, PREG_SET_ORDER))
+ return FALSE;
+
+
+ foreach ($matches as $match_arr)
+ {
+ // split selectors into array
+ $a_keys = $this->_parse_selectors(trim($match_arr[1]));
+
+ // parse each property of an element
+ $codes = explode(";", trim($match_arr[2]));
+ foreach ($codes as $code)
+ {
+ if (strlen(trim($code))>0)
+ {
+ // find the property and the value
+ if (!($sep = strpos($code, ':')))
+ continue;
+
+ $property = strtolower(trim(substr($code, 0, $sep)));
+ $value = trim(substr($code, $sep+1));
+
+ // add the property to the object array
+ foreach ($a_keys as $key)
+ $this->css_data[$key][$property] = $value;
+ }
+ }
+ }
+
+ // clear goups array
+ if (sizeof($matches))
+ {
+ $this->css_groups = array();
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ // split selector group
+ function _parse_selectors($selector)
+ {
+ // trim selector and remove multiple spaces
+ $selector = preg_replace('/\s+/', ' ', trim($selector));
+
+ if (strpos($selector, ','))
+ return preg_split('/[\t\s\n\r]*,[\t\s\n\r]*/mi', $selector);
+ else
+ return array($selector);
+ }
+
+
+ // compare identical styles and make groups
+ function _build_groups()
+ {
+ // clear group array
+ $this->css_groups = array();
+ $string_group_map = array();
+
+ // bulild css string for each selector and check if the same is already defines
+ foreach ($this->css_data as $selector => $prop_arr)
+ {
+ // make shure to compare props in the same order
+ ksort($prop_arr);
+ $compare_str = preg_replace('/[\s\t]+/', '', $this->_style2string($prop_arr, FALSE));
+
+ // add selector to extisting group
+ if (isset($string_group_map[$compare_str]))
+ {
+ $group_index = $string_group_map[$compare_str];
+ $this->css_groups[$group_index]['selectors'][] = $selector;
+ }
+
+ // create new group
+ else
+ {
+ $i = sizeof($this->css_groups);
+ $string_group_map[$compare_str] = $i;
+ $this->css_groups[$i] = array('selectors' => array($selector),
+ 'properties' => $this->css_data[$selector]);
+ }
+ }
+ }
+
+
+ // convert the prop array into a valid css definition
+ function _style2string($prop_arr, $multiline=TRUE)
+ {
+ $out = '';
+ $delm = $multiline ? "\n" : '';
+ $spacer = $multiline ? ' ' : '';
+ $indent = $multiline ? $this->indent_chars : '';
+
+ if (is_array($prop_arr))
+ foreach ($prop_arr as $prop => $value)
+ if (strlen($value))
+ $out .= sprintf('%s%s:%s%s;%s',
+ $indent,
+ $prop,
+ $spacer,
+ $value,
+ $delm);
+
+ return $out;
+ }
+
+
+ // copy all properties inherited from superior styles to a specific selector
+ function _get_inherited_styles($selector, $loop=FALSE)
+ {
+ $css_props = $this->css_data[$selector] ? $this->css_data[$selector] : array();
+
+ // get styles from tag selector
+ if (preg_match('/(([a-z0-9]*)(\.[^\s]+)?)$/i', $selector, $regs))
+ {
+ $sel = $regs[1];
+ $tagname = $regs[2];
+ $class = $regs[3];
+
+ if ($sel && is_array($this->css_data[$sel]))
+ $css_props = $this->_merge_styles($this->css_data[$sel], $css_props);
+
+ if ($class && is_array($this->css_data[$class]))
+ $css_props = $this->_merge_styles($this->css_data[$class], $css_props);
+
+ if ($tagname && is_array($this->css_data[$tagname]))
+ $css_props = $this->_merge_styles($this->css_data[$tagname], $css_props);
+ }
+
+ // analyse inheritance
+ if (strpos($selector, ' '))
+ {
+ $a_hier = split(' ', $selector);
+ if (sizeof($a_hier)>1)
+ {
+ array_pop($a_hier);
+ $base_selector = join(' ', $a_hier);
+
+ // call this method recursively
+ $new_props = $this->_get_inherited_styles($base_selector, TRUE);
+ $css_props = $this->_merge_styles($new_props, $css_props);
+ }
+ }
+
+ // get body style
+ if (!$loop && is_array($this->css_data['body']))
+ $css_props = $this->_merge_styles($this->css_data['body'], $css_props);
+
+ return $css_props;
+ }
+
+
+ // merge two arrays with style properties together like a browser would do
+ function _merge_styles($one, $two)
+ {
+ // these properties are additive
+ foreach (array('text-decoration') as $prop)
+ if ($one[$prop] && $two[$prop])
+ {
+ // if value contains 'none' it's ignored
+ if (strstr($one[$prop], 'none'))
+ continue;
+ else if (strstr($two[$prop], 'none'))
+ unset($two[$prop]);
+
+ $a_values_one = split(' ', $one[$prop]);
+ $a_values_two = split(' ', $two[$prop]);
+ $two[$prop] = join(' ', array_unique(array_merge($a_values_one, $a_values_two)));
+ }
+
+ return array_merge($one, $two);
+ }
+
+
+ // resolve file path
+ function _get_file_path($file)
+ {
+ if (!$this->base_path && $GLOBALS['CSS_PATH'])
+ $this->set_basepath($GLOBALS['CSS_PATH']);
+
+ $base = ($file{0}=='/' || $file{0}=='.' || substr($file, 0, 7)=='http://') ? '' :
+ ($this->base_path ? $this->base_path.'/' : '');
+ return $base.$file;
+ }
+
+ }
+
+
+
+class base_form_element
+ {
+ var $uppertags = FALSE;
+ var $upperattribs = FALSE;
+ var $upperprops = FALSE;
+ var $newline = FALSE;
+
+ var $attrib = array();
+
+
+ // create string with attributes
+ function create_attrib_string($tagname='')
+ {
+ if (!sizeof($this->attrib))
+ return '';
+
+ if ($this->name!='')
+ $this->attrib['name'] = $this->name;
+
+ $attrib_arr = array();
+ foreach ($this->attrib as $key => $value)
+ {
+ // don't output some internally used attributes
+ if (in_array($key, array('form', 'quicksearch')))
+ continue;
+
+ // skip if size if not numeric
+ if (($key=='size' && !is_numeric($value)))
+ continue;
+
+ // skip empty eventhandlers
+ if ((strpos($key,'on')===0 && $value==''))
+ continue;
+
+ // encode textarea content
+ if ($key=='value')
+ $value = rep_specialchars_output($value, 'html', 'replace', FALSE);
+
+ // attributes with no value
+ if (in_array($key, array('checked', 'multiple', 'disabled', 'selected')))
+ {
+ if ($value)
+ $attrib_arr[] = $key;
+ }
+ // don't convert size of value attribute
+ else if ($key=='value')
+ $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $value, 'value');
+
+ // regular tag attributes
+ else
+ $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $this->_conv_case($value, 'value'));
+ }
+
+ return sizeof($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
+ }
+
+
+ // convert tags and attributes to upper-/lowercase
+ // $type can either be "tag" or "attrib"
+ function _conv_case($str, $type='attrib')
+ {
+ if ($type == 'tag')
+ return $this->uppertags ? strtoupper($str) : strtolower($str);
+ else if ($type == 'attrib')
+ return $this->upperattribs ? strtoupper($str) : strtolower($str);
+ else if ($type == 'value')
+ return $this->upperprops ? strtoupper($str) : strtolower($str);
+ }
+ }
+
+
+class input_field extends base_form_element
+ {
+ var $type = 'text';
+
+ // PHP 5 constructor
+ function __construct($attrib=NULL)
+ {
+ if (is_array($attrib))
+ $this->attrib = $attrib;
+
+ if ($attrib['type'])
+ $this->type = $attrib['type'];
+
+ if ($attrib['newline'])
+ $this->newline = TRUE;
+ }
+
+ // PHP 4 compatibility
+ function input_field($attrib=array())
+ {
+ $this->__construct($attrib);
+ }
+
+ // compose input tag
+ function show($value=NULL, $attrib=NULL)
+ {
+ // overwrite object attributes
+ if (is_array($attrib))
+ $this->attrib = array_merge($this->attrib, $attrib);
+
+ // set value attribute
+ if ($value!==NULL)
+ $this->attrib['value'] = $value;
+
+ $this->attrib['type'] = $this->type;
+
+ // return final tag
+ return sprintf('<%s%s />%s',
+ $this->_conv_case('input', 'tag'),
+ $this->create_attrib_string(),
+ ($this->newline ? "\n" : ""));
+ }
+ }
+
+
+class textfield extends input_field
+ {
+ var $type = 'text';
+ }
+
+class passwordfield extends input_field
+ {
+ var $type = 'password';
+ }
+
+class radiobutton extends input_field
+ {
+ var $type = 'radio';
+ }
+
+class checkbox extends input_field
+ {
+ var $type = 'checkbox';
+
+
+ function show($value='', $attrib=NULL)
+ {
+ // overwrite object attributes
+ if (is_array($attrib))
+ $this->attrib = array_merge($this->attrib, $attrib);
+
+ $this->attrib['type'] = $this->type;
+
+ if ($value && (string)$value==(string)$this->attrib['value'])
+ $this->attrib['checked'] = TRUE;
+ else
+ $this->attrib['checked'] = FALSE;
+
+ // return final tag
+ return sprintf('<%s%s />%s',
+ $this->_conv_case('input', 'tag'),
+ $this->create_attrib_string(),
+ ($this->newline ? "\n" : ""));
+ }
+ }
+
+
+class textarea extends base_form_element
+ {
+ // PHP 5 constructor
+ function __construct($attrib=array())
+ {
+ $this->attrib = $attrib;
+
+ if ($attrib['newline'])
+ $this->newline = TRUE;
+ }
+
+ // PHP 4 compatibility
+ function textarea($attrib=array())
+ {
+ $this->__construct($attrib);
+ }
+
+ function show($value='', $attrib=NULL)
+ {
+ // overwrite object attributes
+ if (is_array($attrib))
+ $this->attrib = array_merge($this->attrib, $attrib);
+
+ // take value attribute as content
+ if ($value=='')
+ $value = $this->attrib['value'];
+
+ // make shure we don't print the value attribute
+ if (isset($this->attrib['value']))
+ unset($this->attrib['value']);
+
+ if (strlen($value))
+ $value = rep_specialchars_output($value, 'html', 'replace', FALSE);
+
+ // return final tag
+ return sprintf('<%s%s>%s</%s>%s',
+ $this->_conv_case('textarea', 'tag'),
+ $this->create_attrib_string(),
+ $value,
+ $this->_conv_case('textarea', 'tag'),
+ ($this->newline ? "\n" : ""));
+ }
+ }
+
+
+class hiddenfield extends base_form_element
+ {
+ var $fields_arr = array();
+ var $newline = TRUE;
+
+ // PHP 5 constructor
+ function __construct($attrib=NULL)
+ {
+ if (is_array($attrib))
+ $this->add($attrib);
+ }
+
+ // PHP 4 compatibility
+ function hiddenfield($attrib=NULL)
+ {
+ $this->__construct($attrib);
+ }
+
+ // add a hidden field to this instance
+ function add($attrib)
+ {
+ $this->fields_arr[] = $attrib;
+ }
+
+
+ function show()
+ {
+ $out = '';
+ foreach ($this->fields_arr as $attrib)
+ {
+ $this->attrib = $attrib;
+ $this->attrib['type'] = 'hidden';
+
+ $out .= sprintf('<%s%s />%s',
+ $this->_conv_case('input', 'tag'),
+ $this->create_attrib_string(),
+ ($this->newline ? "\n" : ""));
+ }
+
+ return $out;
+ }
+ }
+
+
+class select extends base_form_element
+ {
+ var $options = array();
+
+ /*
+ syntax:
+ -------
+ // create instance. arguments are used to set attributes of select-tag
+ $select = new select(array('name' => 'fieldname'));
+
+ // add one option
+ $select->add('Switzerland', 'CH');
+
+ // add multiple options
+ $select->add(array('Switzerland', 'Germany'),
+ array('CH', 'DE'));
+
+ // add 10 blank options with 50 chars
+ // used to fill with javascript (necessary for 4.x browsers)
+ $select->add_blank(10, 50);
+
+ // generate pulldown with selection 'Switzerland' and return html-code
+ // as second argument the same attributes available to instanciate can be used
+ print $select->show('CH');
+ */
+
+ // PHP 5 constructor
+ function __construct($attrib=NULL)
+ {
+ if (is_array($attrib))
+ $this->attrib = $attrib;
+
+ if ($attrib['newline'])
+ $this->newline = TRUE;
+ }
+
+ // PHP 4 compatibility
+ function select($attrib=NULL)
+ {
+ $this->__construct($attrib);
+ }
+
+
+ function add($names, $values=NULL)
+ {
+ if (is_array($names))
+ {
+ foreach ($names as $i => $text)
+ $this->options[] = array('text' => $text, 'value' => (string)$values[$i]);
+ }
+ else
+ {
+ $this->options[] = array('text' => $names, 'value' => (string)$values);
+ }
+ }
+
+
+ function add_blank($nr, $width=0)
+ {
+ $text = $width ? str_repeat(' ', $width) : '';
+
+ for ($i=0; $i < $nr; $i++)
+ $this->options[] = array('text' => $text);
+ }
+
+
+ function show($select=array(), $attrib=NULL)
+ {
+ $options_str = "\n";
+ $value_str = $this->_conv_case(' value="%s"', 'attrib');
+
+ if (!is_array($select))
+ $select = array((string)$select);
+
+ foreach ($this->options as $option)
+ {
+ $selected = ((strlen($option['value']) && in_array($option['value'], $select, TRUE)) ||
+ (in_array($option['text'], $select, TRUE))) ? $this->_conv_case(' selected', 'attrib') : '';
+
+ $options_str .= sprintf("<%s%s%s>%s</%s>\n",
+ $this->_conv_case('option', 'tag'),
+ strlen($option['value']) ? sprintf($value_str, $option['value']) : '',
+ $selected,
+ rep_specialchars_output($option['text'], 'html', 'replace', FALSE),
+ $this->_conv_case('option', 'tag'));
+ }
+
+ // return final tag
+ return sprintf('<%s%s>%s</%s>%s',
+ $this->_conv_case('select', 'tag'),
+ $this->create_attrib_string(),
+ $options_str,
+ $this->_conv_case('select', 'tag'),
+ ($this->newline ? "\n" : ""));
+ }
+ }
+
+
+
+
+// ********* rcube schared functions *********
+
+
+// provide details about the client's browser
+function rcube_browser()
+ {
+ $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
+
+ $bw['ver'] = 0;
+ $bw['win'] = stristr($HTTP_USER_AGENT, 'win');
+ $bw['mac'] = stristr($HTTP_USER_AGENT, 'mac');
+ $bw['linux'] = stristr($HTTP_USER_AGENT, 'linux');
+ $bw['unix'] = stristr($HTTP_USER_AGENT, 'unix');
+
+ $bw['ns4'] = stristr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
+ $bw['ns'] = ($bw['ns4'] || stristr($HTTP_USER_AGENT, 'netscape'));
+ $bw['ie'] = stristr($HTTP_USER_AGENT, 'msie');
+ $bw['mz'] = stristr($HTTP_USER_AGENT, 'mozilla/5');
+ $bw['opera'] = stristr($HTTP_USER_AGENT, 'opera');
+ $bw['safari'] = stristr($HTTP_USER_AGENT, 'safari');
+
+ if($bw['ns'])
+ {
+ $test = eregi("mozilla\/([0-9\.]+)", $HTTP_USER_AGENT, $regs);
+ $bw['ver'] = $test ? (float)$regs[1] : 0;
+ }
+ if($bw['mz'])
+ {
+ $test = ereg("rv:([0-9\.]+)", $HTTP_USER_AGENT, $regs);
+ $bw['ver'] = $test ? (float)$regs[1] : 0;
+ }
+ if($bw['ie'])
+ {
+ $test = eregi("msie ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
+ $bw['ver'] = $test ? (float)$regs[1] : 0;
+ }
+ if($bw['opera'])
+ {
+ $test = eregi("opera ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
+ $bw['ver'] = $test ? (float)$regs[1] : 0;
+ }
+
+ if(eregi(" ([a-z]{2})-([a-z]{2})", $HTTP_USER_AGENT, $regs))
+ $bw['lang'] = $regs[1];
+ else
+ $bw['lang'] = 'en';
+
+ $bw['dom'] = ($bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5) || ($bw['opera'] && $bw['ver']>=7));
+ $bw['pngalpha'] = $bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5.5) ||
+ ($bw['ie'] && $bw['ver']>=5 && $bw['mac']) || ($bw['opera'] && $bw['ver']>=7) ? TRUE : FALSE;
+
+ return $bw;
+ }
+
+
+// get text in the desired language from the language file
+function rcube_label($attrib)
+ {
+ global $sess_user_lang, $INSTALL_PATH, $OUTPUT;
+ static $sa_text_data, $s_language, $utf8_decode;
+
+ // extract attributes
+ if (is_string($attrib))
+ $attrib = array('name' => $attrib);
+
+ $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
+ $vars = isset($attrib['vars']) ? $attrib['vars'] : '';
+
+ $command_name = strlen($attrib['command']) ? $attrib['command'] : NULL;
+ $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
+
+
+ // load localized texts
+ if (!$sa_text_data || $s_language != $sess_user_lang)
+ {
+ $sa_text_data = array();
+
+ // get english labels (these should be complete)
+ @include($INSTALL_PATH.'program/localization/en_US/labels.inc');
+ @include($INSTALL_PATH.'program/localization/en_US/messages.inc');
+
+ if (is_array($labels))
+ $sa_text_data = $labels;
+ if (is_array($messages))
+ $sa_text_data = array_merge($sa_text_data, $messages);
+
+ // include user language files
+ if ($sess_user_lang!='en' && is_dir($INSTALL_PATH.'program/localization/'.$sess_user_lang))
+ {
+ include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/labels.inc');
+ include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/messages.inc');
+
+ if (is_array($labels))
+ $sa_text_data = array_merge($sa_text_data, $labels);
+ if (is_array($messages))
+ $sa_text_data = array_merge($sa_text_data, $messages);
+ }
+
+ $s_language = $sess_user_lang;
+ }
+
+ // text does not exist
+ if (!($text_item = $sa_text_data[$alias]))
+ {
+ /*
+ raise_error(array('code' => 500,
+ 'type' => 'php',
+ 'line' => __LINE__,
+ 'file' => __FILE__,
+ 'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
+ */
+ return "[$alias]";
+ }
+
+ // make text item array
+ $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
+
+ // decide which text to use
+ if ($nr==1)
+ $text = $a_text_item['single'];
+ else if ($nr>0)
+ $text = $a_text_item['multiple'];
+ else if ($nr==0)
+ {
+ if ($a_text_item['none'])
+ $text = $a_text_item['none'];
+ else if ($a_text_item['single'])
+ $text = $a_text_item['single'];
+ else if ($a_text_item['multiple'])
+ $text = $a_text_item['multiple'];
+ }
+
+ // default text is single
+ if ($text=='')
+ $text = $a_text_item['single'];
+
+ // replace vars in text
+ if (is_array($attrib['vars']))
+ {
+ foreach ($attrib['vars'] as $var_key=>$var_value)
+ $a_replace_vars[substr($var_key, 0, 1)=='$' ? substr($var_key, 1) : $var_key] = $var_value;
+ }
+
+ if ($a_replace_vars)
+ $text = preg_replace('/\${?([_a-z]{1}[_a-z0-9]*)}?/ei', '$a_replace_vars["\1"]', $text);
+
+ // remove variables in text which were not available in arg $vars and $nr
+ eval("\$text = <<<EOF
+$text
+EOF;
+");
+
+ // format output
+ if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
+ return ucfirst($text);
+ else if ($attrib['uppercase'])
+ return strtoupper($text);
+ else if ($attrib['lowercase'])
+ return strtolower($text);
+ else
+ return $text;
+
+ return $text;
+ }
+
+
+// send HTTP header for no-cacheing steps
+function send_nocacheing_headers()
+ {
+ if (headers_sent())
+ return;
+
+ header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");
+ header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
+ header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
+ header("Pragma: no-cache");
+ }
+
+
+// send header with expire date 30 days in future
+function send_future_expire_header()
+ {
+ if (!headers_sent())
+ header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+2600000)." GMT");
+ }
+
+
+// function to convert an array to a javascript array
+function array2js($arr, $type='')
+ {
+ if (!$type)
+ $type = 'mixed';
+
+ if (is_array($arr))
+ {
+ // no items in array
+ if (!sizeof($arr))
+ return 'new Array()';
+ else
+ {
+ $a_pairs = array();
+ $keys_arr = array_keys($arr);
+ $is_assoc = $have_numeric = 0;
+
+ for ($i=0; $i<sizeof($keys_arr); ++$i)
+ {
+ if(is_numeric($keys_arr[$i]))
+ $have_numeric = 1;
+ if (!is_numeric($keys_arr[$i]) || $keys_arr[$i]!=$i)
+ $is_assoc = 1;
+ if($is_assoc && $have_numeric)
+ break;
+ }
+
+ $previous_was_array = false;
+ while (list($key, $value) = each($arr))
+ {
+ // enclose key with quotes if it is not variable-name conform
+ if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */)
+ $key = "'$key'";
+
+ if (!is_array($value))
+ {
+ $value = str_replace("\r\n", '\n', $value);
+ $value = str_replace("\n", '\n', $value);
+ }
+
+ $is_string = false;
+ if (!is_array($value))
+ {
+ if ($type=='string')
+ $is_string = true;
+ else if ((($type=='mixed' && is_numeric($value)) || $type=='int') && strlen($value)<16) // js interprets numbers with digits >15 as ...e+...
+ $is_string = FALSE;
+ else
+ $is_string = TRUE;
+ }
+
+ if ($is_string)
+ $value = "'".preg_replace("/(?<!\\\)'/", "\'", $value)."'";
+
+ $a_pairs[] = sprintf("%s%s",
+ $is_assoc ? "$key:" : '',
+ is_array($value) ? array2js($value, $type) : $value);
+ }
+
+ if ($a_pairs)
+ {
+ if ($is_assoc)
+ $return = '{'.implode(',', $a_pairs).'}';
+ else
+ $return = '['.implode(',', $a_pairs).']';
+ }
+
+ return $return;
+ }
+ }
+ else
+ return $arr;
+ }
+
+
+// similar function as in_array() ut case-insensitive
+function in_array_nocase($needle, $haystack)
+ {
+ foreach ($haystack as $value)
+ {
+ if (strtolower($needle)===strtolower($value))
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+
+// find out if the string content means TRUE or FALSE
+function get_boolean($str)
+ {
+ $str = strtolower($str);
+ if(in_array($str, array('false', '0', 'no', 'nein', ''), TRUE))
+ return FALSE;
+ else
+ return TRUE;
+ }
+
+
+function show_bytes($numbytes)
+ {
+ if ($numbytes > 1024)
+ return sprintf('%d KB', round($numbytes/1024));
+ else
+ return sprintf('%d B', $numbytes);
+ }
+
+
+// convert paths like ../xxx to an absolute path using a base url
+function make_absolute_url($path, $base_url)
+ {
+ $host_url = $base_url;
+ $abs_path = $path;
+
+ // cut base_url to the last directory
+ if (strpos($base_url, '/')>7)
+ {
+ $host_url = substr($base_url, 0, strpos($base_url, '/'));
+ $base_url = substr($base_url, 0, strrpos($base_url, '/'));
+ }
+
+ // $path is absolute
+ if ($path{0}=='/')
+ $abs_path = $host_url.$path;
+ else
+ {
+ // strip './' because its the same as ''
+ $path = preg_replace('/^\.\//', '', $path);
+
+ if(preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER))
+ foreach($matches as $a_match)
+ {
+ if (strrpos($base_url, '/'))
+ $base_url = substr($base_url, 0, strrpos($base_url, '/'));
+
+ $path = substr($path, 3);
+ }
+
+ $abs_path = $base_url.'/'.$path;
+ }
+
+ return $abs_path;
+ }
+
+
+// replace the middle part of a string with ...
+// if it is longer than the allowed length
+function abbrevate_string($str, $maxlength, $place_holder='...')
+ {
+ $length = strlen($str);
+ $first_part_length = floor($maxlength/2) - strlen($place_holder);
+
+ if ($length > $maxlength)
+ {
+ $second_starting_location = $length - $maxlength + $first_part_length + 1;
+ $str = substr($str, 0, $first_part_length) . $place_holder . substr($str, $second_starting_location, $length);
+ }
+
+ return $str;
+ }
+
+
+// make sure the string ends with a slash
+function slashify($str)
+ {
+ return unslashify($str).'/';
+ }
+
+
+// remove slash at the end of the string
+function unslashify($str)
+ {
+ return preg_replace('/\/$/', '', $str);
+ }
+
+
+// delete all files within a folder
+function clear_directory($dir_path)
+ {
+ $dir = @opendir($dir_path);
+ if(!$dir) return FALSE;
+
+ while ($file = readdir($dir))
+ if (strlen($file)>2)
+ unlink("$dir_path/$file");
+
+ closedir($dir);
+ return TRUE;
+ }
+
+
+// create a unix timestamp with a specified offset from now
+function get_offset_time($offset_str, $factor=1)
+ {
+ if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs))
+ {
+ $amount = (int)$regs[1];
+ $unit = strtolower($regs[2]);
+ }
+ else
+ {
+ $amount = (int)$offset_str;
+ $unit = 's';
+ }
+
+ $ts = mktime();
+ switch ($unit)
+ {
+ case 'w':
+ $amount *= 7;
+ case 'd':
+ $amount *= 24;
+ case 'h':
+ $amount *= 60;
+ case 'h':
+ $amount *= 60;
+ case 's':
+ $ts += $amount * $factor;
+ }
+
+ return $ts;
+ }
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_smtp.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide SMTP functionality using socket connections |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_smtp.inc 266 2006-06-26 18:38:03Z thomasb $
+
+*/
+
+
+// include required PEAR classes
+require_once('Net/SMTP.php');
+
+
+// define headers delimiter
+define('SMTP_MIME_CRLF', "\r\n");
+
+$SMTP_CONN = null;
+
+/**
+ * Function for sending mail using SMTP.
+ *
+ * @param string Sender e-Mail address
+ *
+ * @param mixed Either a comma-seperated list of recipients
+ * (RFC822 compliant), or an array of recipients,
+ * each RFC822 valid. This may contain recipients not
+ * specified in the headers, for Bcc:, resending
+ * messages, etc.
+ *
+ * @param mixed The message headers to send with the mail
+ * Either as an associative array or a finally
+ * formatted string
+ *
+ * @param string The full text of the message body, including any Mime parts, etc.
+ *
+ * @return bool Returns TRUE on success, or FALSE on error
+ * @access public
+ */
+function smtp_mail($from, $recipients, &$headers, &$body)
+ {
+ global $SMTP_CONN, $CONFIG, $SMTP_ERROR;
+ $smtp_timeout = null;
+ $smtp_host = $CONFIG['smtp_server'];
+ $smtp_port = is_numeric($CONFIG['smtp_port']) ? $CONFIG['smtp_port'] : 25;
+ $smtp_host_url = parse_url($CONFIG['smtp_server']);
+
+ // overwrite port
+ if ($smtp_host_url['host'] && $smtp_host_url['port'])
+ {
+ $smtp_host = $smtp_host_url['host'];
+ $smtp_port = $smtp_host_url['port'];
+ }
+
+ // re-write smtp host
+ if ($smtp_host_url['host'] && $smtp_host_url['scheme'])
+ $smtp_host = sprintf('%s://%s', $smtp_host_url['scheme'], $smtp_host_url['host']);
+
+
+ // create Net_SMTP object and connect to server
+ if (!is_object($smtp_conn))
+ {
+ $helo_host = !empty($_SERVER['server_name']) ? $_SERVER['server_name'] : 'localhost';
+ $SMTP_CONN = new Net_SMTP($smtp_host, $smtp_port, $helo_host);
+
+ // set debugging
+ if ($CONFIG['debug_level'] & 8)
+ $SMTP_CONN->setDebug(TRUE);
+
+
+ // try to connect to server and exit on failure
+ $result = $SMTP_CONN->connect($smtp_timeout);
+ if (PEAR::isError($result))
+ {
+ $SMTP_CONN = null;
+ $SMTP_ERROR .= "Connection failed: ".$result->getMessage()."\n";
+ return FALSE;
+ }
+
+ // attempt to authenticate to the SMTP server
+ if ($CONFIG['smtp_user'] && $CONFIG['smtp_pass'])
+ {
+ if (strstr($CONFIG['smtp_user'], '%u'))
+ $smtp_user = str_replace('%u', $_SESSION['username'], $CONFIG['smtp_user']);
+ else
+ $smtp_user = $CONFIG['smtp_user'];
+
+ if (strstr($CONFIG['smtp_pass'], '%p'))
+ $smtp_pass = str_replace('%p', decrypt_passwd($_SESSION['password']), $CONFIG['smtp_pass']);
+ else
+ $smtp_pass = $CONFIG['smtp_pass'];
+
+ $smtp_auth_type = empty($CONFIG['smtp_auth_type']) ? NULL : $CONFIG['smtp_auth_type'];
+ $result = $SMTP_CONN->auth($smtp_user, $smtp_pass, $smtp_auth_type);
+
+ if (PEAR::isError($result))
+ {
+ smtp_reset();
+ $SMTP_ERROR .= "Authentication failure: ".$result->getMessage()."\n";
+ return FALSE;
+ }
+ }
+ }
+
+
+ // prepare message headers as string
+ if (is_array($headers))
+ {
+ $headerElements = smtp_prepare_headers($headers);
+ if (!$headerElements)
+ {
+ smtp_reset();
+ return FALSE;
+ }
+
+ list($from, $text_headers) = $headerElements;
+ }
+ else if (is_string($headers))
+ $text_headers = $headers;
+ else
+ {
+ smtp_reset();
+ $SMTP_ERROR .= "Invalid message headers\n";
+ return FALSE;
+ }
+
+ // exit if no from address is given
+ if (!isset($from))
+ {
+ smtp_reset();
+ $SMTP_ERROR .= "No From address has been provided\n";
+ return FALSE;
+ }
+
+
+ // set From: address
+ if (PEAR::isError($SMTP_CONN->mailFrom($from)))
+ {
+ smtp_reset();
+ $SMTP_ERROR .= "Failed to set sender '$from'\n";
+ return FALSE;
+ }
+
+
+ // prepare list of recipients
+ $recipients = smtp_parse_rfc822($recipients);
+ if (PEAR::isError($recipients))
+ {
+ smtp_reset();
+ return FALSE;
+ }
+
+
+ // set mail recipients
+ foreach ($recipients as $recipient)
+ {
+ if (PEAR::isError($SMTP_CONN->rcptTo($recipient)))
+ {
+ smtp_reset();
+ $SMTP_ERROR .= "Failed to add recipient '$recipient'\n";
+ return FALSE;
+ }
+ }
+
+
+ // Send the message's headers and the body as SMTP data.
+ if (PEAR::isError($SMTP_CONN->data("$text_headers\r\n$body")))
+ {
+ smtp_reset();
+ $SMTP_ERROR .= "Failed to send data\n";
+ return FALSE;
+ }
+
+
+ return TRUE;
+ }
+
+
+
+/**
+ * Reset the global SMTP connection
+ * @access public
+ */
+function smtp_reset()
+ {
+ global $SMTP_CONN;
+
+ if (is_object($SMTP_CONN))
+ {
+ $SMTP_CONN->rset();
+ smtp_disconnect();
+ }
+ }
+
+
+
+/**
+ * Disconnect the global SMTP connection and destroy object
+ * @access public
+ */
+function smtp_disconnect()
+ {
+ global $SMTP_CONN;
+
+ if (is_object($SMTP_CONN))
+ {
+ $SMTP_CONN->disconnect();
+ $SMTP_CONN = null;
+ }
+ }
+
+
+/**
+ * Take an array of mail headers and return a string containing
+ * text usable in sending a message.
+ *
+ * @param array $headers The array of headers to prepare, in an associative
+ * array, where the array key is the header name (ie,
+ * 'Subject'), and the array value is the header
+ * value (ie, 'test'). The header produced from those
+ * values would be 'Subject: test'.
+ *
+ * @return mixed Returns false if it encounters a bad address,
+ * otherwise returns an array containing two
+ * elements: Any From: address found in the headers,
+ * and the plain text version of the headers.
+ * @access private
+ */
+function smtp_prepare_headers($headers)
+ {
+ $lines = array();
+ $from = null;
+
+ foreach ($headers as $key => $value)
+ {
+ if (strcasecmp($key, 'From') === 0)
+ {
+ $addresses = smtp_parse_rfc822($value);
+
+ if (is_array($addresses))
+ $from = $addresses[0];
+
+ // Reject envelope From: addresses with spaces.
+ if (strstr($from, ' '))
+ return FALSE;
+
+
+ $lines[] = $key . ': ' . $value;
+ }
+ else if (strcasecmp($key, 'Received') === 0)
+ {
+ $received = array();
+ if (is_array($value))
+ {
+ foreach ($value as $line)
+ $received[] = $key . ': ' . $line;
+ }
+ else
+ {
+ $received[] = $key . ': ' . $value;
+ }
+
+ // Put Received: headers at the top. Spam detectors often
+ // flag messages with Received: headers after the Subject:
+ // as spam.
+ $lines = array_merge($received, $lines);
+ }
+
+ else
+ {
+ // If $value is an array (i.e., a list of addresses), convert
+ // it to a comma-delimited string of its elements (addresses).
+ if (is_array($value))
+ $value = implode(', ', $value);
+
+ $lines[] = $key . ': ' . $value;
+ }
+ }
+
+ return array($from, join(SMTP_MIME_CRLF, $lines) . SMTP_MIME_CRLF);
+ }
+
+
+
+/**
+ * Take a set of recipients and parse them, returning an array of
+ * bare addresses (forward paths) that can be passed to sendmail
+ * or an smtp server with the rcpt to: command.
+ *
+ * @param mixed Either a comma-seperated list of recipients
+ * (RFC822 compliant), or an array of recipients,
+ * each RFC822 valid.
+ *
+ * @return array An array of forward paths (bare addresses).
+ * @access private
+ */
+function smtp_parse_rfc822($recipients)
+ {
+ // if we're passed an array, assume addresses are valid and implode them before parsing.
+ if (is_array($recipients))
+ $recipients = implode(', ', $recipients);
+
+ $addresses = array();
+ $recipients = smtp_explode_quoted_str(",", $recipients);
+
+ reset($recipients);
+ while (list($k, $recipient) = each($recipients))
+ {
+ $a = explode(" ", $recipient);
+ while (list($k2, $word) = each($a))
+ {
+ if ((strpos($word, "@") > 0) && (strpos($word, "\"")===false))
+ {
+ $word = ereg_replace('^<|>$', '', trim($word));
+ if (in_array($word, $addresses)===false)
+ array_push($addresses, $word);
+ }
+ }
+ }
+ return $addresses;
+ }
+
+
+function smtp_explode_quoted_str($delimiter, $string)
+ {
+ $quotes=explode("\"", $string);
+ while ( list($key, $val) = each($quotes))
+ if (($key % 2) == 1)
+ $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
+ $string=implode("\"", $quotes);
+
+ $result=explode($delimiter, $string);
+ while (list($key, $val) = each($result))
+ $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]);
+
+ return $result;
+ }
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_sqlite.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide callback functions for sqlite that will emulate |
+ | sone MySQL functions |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_sqlite.inc 88 2005-12-03 16:54:12Z roundcube $
+
+*/
+
+
+function rcube_sqlite_from_unixtime($timestamp)
+ {
+ $timestamp = trim($timestamp);
+ if (!preg_match("/^[0-9]+$/is", $timestamp))
+ $ret = strtotime($timestamp);
+ else
+ $ret = $timestamp;
+
+ $ret = date("Y-m-d H:i:s", $ret);
+ rcube_sqlite_debug("FROM_UNIXTIME ($timestamp) = $ret");
+ return $ret;
+ }
+
+
+function rcube_sqlite_unix_timestamp($timestamp="")
+ {
+ $timestamp = trim($timestamp);
+ if (!$timestamp)
+ $ret = time();
+ else if (!preg_match("/^[0-9]+$/is", $timestamp))
+ $ret = strtotime($timestamp);
+ else
+ $ret = $timestamp;
+
+ rcube_sqlite_debug("UNIX_TIMESTAMP ($timestamp) = $ret");
+ return $ret;
+ }
+
+
+function rcube_sqlite_now()
+ {
+ rcube_sqlite_debug("NOW() = ".date("Y-m-d H:i:s"));
+ return date("Y-m-d H:i:s");
+ }
+
+
+function rcube_sqlite_md5($str)
+ {
+ return md5($str);
+ }
+
+
+function rcube_sqlite_debug($str)
+ {
+ //console($str);
+ }
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/session.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev, - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide database supported session management |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: session.inc 132 2006-02-05 15:38:51Z roundcube $
+
+*/
+
+
+function sess_open($save_path, $session_name)
+ {
+ return TRUE;
+ }
+
+
+
+function sess_close()
+ {
+ return TRUE;
+ }
+
+
+// read session data
+function sess_read($key)
+ {
+ global $DB, $SESS_CHANGED;
+
+ $sql_result = $DB->query("SELECT vars, ip, ".$DB->unixtimestamp('changed')." AS changed
+ FROM ".get_table_name('session')."
+ WHERE sess_id=?",
+ $key);
+
+ if ($sql_arr = $DB->fetch_assoc($sql_result))
+ {
+ $SESS_CHANGED = mktime(); //$sql_arr['changed'];
+
+ if (strlen($sql_arr['vars']))
+ return $sql_arr['vars'];
+ }
+
+ return FALSE;
+ }
+
+
+// save session data
+function sess_write($key, $vars)
+ {
+ global $DB;
+
+ $sql_result = $DB->query("SELECT 1
+ FROM ".get_table_name('session')."
+ WHERE sess_id=?",
+ $key);
+
+ if ($DB->num_rows($sql_result))
+ {
+ session_decode($vars);
+ $DB->query("UPDATE ".get_table_name('session')."
+ SET vars=?,
+ changed=now()
+ WHERE sess_id=?",
+ $vars,
+ $key);
+ }
+ else
+ {
+ $DB->query("INSERT INTO ".get_table_name('session')."
+ (sess_id, vars, ip, created, changed)
+ VALUES (?, ?, ?, now(), now())",
+ $key,
+ $vars,
+ $_SERVER['REMOTE_ADDR']);
+
+
+ }
+
+ return TRUE;
+ }
+
+
+// handler for session_destroy()
+function sess_destroy($key)
+ {
+ global $DB;
+
+ // delete session entries in cache table
+ $DB->query("DELETE FROM ".get_table_name('cache')."
+ WHERE session_id=?",
+ $key);
+
+ $DB->query("DELETE FROM ".get_table_name('session')."
+ WHERE sess_id=?",
+ $key);
+
+ rcmail_clear_session_temp($key);
+ return TRUE;
+ }
+
+
+// garbage collecting function
+function sess_gc($maxlifetime)
+ {
+ global $DB;
+
+ // get all expired sessions
+ $sql_result = $DB->query("SELECT sess_id
+ FROM ".get_table_name('session')."
+ WHERE ".$DB->unixtimestamp('now()')."-".$DB->unixtimestamp('changed')." > ?",
+ $maxlifetime);
+
+ $a_exp_sessions = array();
+ while ($sql_arr = $DB->fetch_assoc($sql_result))
+ $a_exp_sessions[] = $sql_arr['sess_id'];
+
+
+ if (sizeof($a_exp_sessions))
+ {
+ // delete session cache records
+ $DB->query("DELETE FROM ".get_table_name('cache')."
+ WHERE session_id IN ('".join("','", $a_exp_sessions)."')");
+
+ // delete session records
+ $DB->query("DELETE FROM ".get_table_name('session')."
+ WHERE sess_id IN ('".join("','", $a_exp_sessions)."')");
+ }
+
+ // remove session specific temp dirs
+ foreach ($a_exp_sessions as $key)
+ rcmail_clear_session_temp($key);
+
+ // also run message cache GC
+ rcmail_message_cache_gc();
+
+ return TRUE;
+ }
+
+
+// set custom functions for PHP session management
+session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy', 'sess_gc');
+
+?>
--- /dev/null
+/*
+ +-----------------------------------------------------------------------+
+ | RoundCube Webmail Client Script |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev, - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Authors: Thomas Bruederli <roundcube@gmail.com> |
+ | Charles McNulty <charles@charlesmcnulty.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: app.js 431 2006-12-23 10:44:16Z thomasb $
+*/
+
+// Constants
+var CONTROL_KEY = 1;
+var SHIFT_KEY = 2;
+var CONTROL_SHIFT_KEY = 3;
+
+var rcube_webmail_client;
+
+function rcube_webmail()
+ {
+ this.env = new Object();
+ this.labels = new Object();
+ this.buttons = new Object();
+ this.gui_objects = new Object();
+ this.commands = new Object();
+ this.selection = new Array();
+ this.last_selected = 0;
+ this.in_message_list = false;
+
+ // create public reference to myself
+ rcube_webmail_client = this;
+ this.ref = 'rcube_webmail_client';
+
+ // webmail client settings
+ this.dblclick_time = 600;
+ this.message_time = 5000;
+
+ this.mbox_expression = new RegExp('[^0-9a-z\-_]', 'gi');
+
+ // mimetypes supported by the browser (default settings)
+ this.mimetypes = new Array('text/plain', 'text/html', 'text/xml',
+ 'image/jpeg', 'image/gif', 'image/png',
+ 'application/x-javascript', 'application/pdf',
+ 'application/x-shockwave-flash');
+
+ // default environment vars
+ this.env.keep_alive = 60; // seconds
+ this.env.request_timeout = 180; // seconds
+ this.env.draft_autosave = 300; // seconds
+
+
+ // set environment variable
+ this.set_env = function(name, value)
+ {
+ this.env[name] = value;
+ };
+
+
+ // add a localized label to the client environment
+ this.add_label = function(key, value)
+ {
+ this.labels[key] = value;
+ };
+
+
+ // add a button to the button list
+ this.register_button = function(command, id, type, act, sel, over)
+ {
+ if (!this.buttons[command])
+ this.buttons[command] = new Array();
+
+ var button_prop = {id:id, type:type};
+ if (act) button_prop.act = act;
+ if (sel) button_prop.sel = sel;
+ if (over) button_prop.over = over;
+
+ this.buttons[command][this.buttons[command].length] = button_prop;
+ };
+
+
+ // register a specific gui object
+ this.gui_object = function(name, id)
+ {
+ this.gui_objects[name] = id;
+ };
+
+
+ // initialize webmail client
+ this.init = function()
+ {
+ this.task = this.env.task;
+
+ // check browser
+ if (!bw.dom || !bw.xmlhttp_test())
+ {
+ location.href = this.env.comm_path+'&_action=error&_code=0x199';
+ return;
+ }
+
+ // find all registered gui objects
+ for (var n in this.gui_objects)
+ this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
+
+ // tell parent window that this frame is loaded
+ if (this.env.framed && parent.rcmail && parent.rcmail.set_busy)
+ parent.rcmail.set_busy(false);
+
+ // enable general commands
+ this.enable_command('logout', 'mail', 'addressbook', 'settings', true);
+
+ switch (this.task)
+ {
+ case 'mail':
+ var msg_list_frame = this.gui_objects.mailcontframe;
+ var msg_list = this.gui_objects.messagelist;
+ if (msg_list)
+ {
+ msg_list_frame.onmousedown = function(e){return rcube_webmail_client.click_on_list(e);};
+ this.init_messagelist(msg_list);
+ this.enable_command('toggle_status', true);
+ }
+
+ // enable mail commands
+ this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', true);
+
+ if (this.env.action=='show')
+ {
+ this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'viewsource', 'print', 'load-attachment', true);
+ if (this.env.next_uid)
+ this.enable_command('nextmessage', true);
+ if (this.env.prev_uid)
+ this.enable_command('previousmessage', true);
+ }
+
+ if (this.env.action=='show' && this.env.blockedobjects)
+ {
+ if (this.gui_objects.remoteobjectsmsg)
+ this.gui_objects.remoteobjectsmsg.style.display = 'block';
+ this.enable_command('load-images', true);
+ }
+
+ if (this.env.action=='compose')
+ {
+ this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true);
+ if (this.env.spellcheck)
+ this.enable_command('spellcheck', true);
+ if (this.env.drafts_mailbox)
+ this.enable_command('savedraft', true);
+ }
+
+ if (this.env.messagecount)
+ this.enable_command('select-all', 'select-none', 'sort', 'expunge', true);
+
+ if (this.env.messagecount && (this.env.mailbox==this.env.trash_mailbox || this.env.mailbox==this.env.junk_mailbox))
+ this.enable_command('purge', true);
+
+ this.set_page_buttons();
+
+ // focus this window
+ window.focus();
+
+ // init message compose form
+ if (this.env.action=='compose')
+ this.init_messageform();
+
+ // show printing dialog
+ if (this.env.action=='print')
+ window.print();
+
+ // get unread count for each mailbox
+ if (this.gui_objects.mailboxlist)
+ this.http_request('getunread', '');
+
+ break;
+
+
+ case 'addressbook':
+ var contacts_list = this.gui_objects.contactslist;
+ var ldap_contacts_list = this.gui_objects.ldapcontactslist;
+
+ if (contacts_list)
+ this.init_contactslist(contacts_list);
+
+ if (ldap_contacts_list)
+ this.init_ldapsearchlist(ldap_contacts_list);
+
+ this.set_page_buttons();
+
+ if (this.env.cid)
+ this.enable_command('show', 'edit', true);
+
+ if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform)
+ this.enable_command('save', true);
+
+ this.enable_command('list', 'add', true);
+
+ this.enable_command('ldappublicsearch', this.env.ldappublicsearch);
+
+ break;
+
+
+ case 'settings':
+ this.enable_command('preferences', 'identities', 'save', 'folders', true);
+
+ if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity')
+ this.enable_command('edit', 'add', 'delete', true);
+
+ if (this.env.action=='edit-identity' || this.env.action=='add-identity')
+ this.enable_command('save', true);
+
+ if (this.env.action=='folders')
+ this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true);
+
+ var identities_list = this.gui_objects.identitieslist;
+ if (identities_list)
+ this.init_identitieslist(identities_list);
+
+ break;
+
+ case 'login':
+ var input_user = rcube_find_object('_user');
+ var input_pass = rcube_find_object('_pass');
+ if (input_user && input_user.value=='')
+ input_user.focus();
+ else if (input_pass)
+ input_pass.focus();
+
+ this.enable_command('login', true);
+ break;
+
+ default:
+ break;
+ }
+
+
+ // enable basic commands
+ this.enable_command('logout', true);
+
+ // disable browser's contextmenus
+ // document.oncontextmenu = function(){ return false; }
+
+ // load body click event
+ document.onmousedown = function(){ return rcube_webmail_client.reset_click(); };
+ document.onkeydown = function(e){ return rcube_webmail_client.key_pressed(e, msg_list_frame); };
+
+ // flag object as complete
+ this.loaded = true;
+
+ // show message
+ if (this.pending_message)
+ this.display_message(this.pending_message[0], this.pending_message[1]);
+
+ // start keep-alive interval
+ this.start_keepalive();
+ };
+
+
+ // start interval for keep-alive/recent_check signal
+ this.start_keepalive = function()
+ {
+ if (this.env.keep_alive && this.task=='mail' && this.gui_objects.messagelist)
+ this._int = setInterval(this.ref+'.check_for_recent()', this.env.keep_alive * 1000);
+ else if (this.env.keep_alive && this.task!='login')
+ this._int = setInterval(this.ref+'.send_keep_alive()', this.env.keep_alive * 1000);
+ }
+
+
+ // reset last clicked if user clicks on anything other than the message table
+ this.reset_click = function()
+ {
+ var id;
+ this.in_message_list = false;
+ for (var n=0; n<this.selection.length; n++)
+ {
+ id = this.selection[n];
+ if (this.list_rows[id] && this.list_rows[id].obj)
+ {
+ this.set_classname(this.list_rows[id].obj, 'selected', false);
+ this.set_classname(this.list_rows[id].obj, 'unfocused', true);
+ }
+ }
+ };
+
+ this.click_on_list = function(e)
+ {
+ if (!e)
+ e = window.event;
+
+ for (var n=0; n<this.selection.length; n++)
+ {
+ id = this.selection[n];
+ if (this.list_rows[id].obj)
+ {
+ this.set_classname(this.list_rows[id].obj, 'selected', true);
+ this.set_classname(this.list_rows[id].obj, 'unfocused', false);
+ }
+ }
+
+ var mbox_li;
+ if (mbox_li = this.get_mailbox_li())
+ this.set_classname(mbox_li, 'unfocused', true);
+
+ this.in_message_list = true;
+ e.cancelBubble = true;
+ };
+
+ this.key_pressed = function(e, msg_list_frame) {
+ if (this.in_message_list != true)
+ return true;
+ var keyCode = document.layers ? e.which : document.all ? event.keyCode : document.getElementById ? e.keyCode : 0;
+ var mod_key = this.get_modifier(e);
+ switch (keyCode) {
+ case 13:
+ this.command('show','',this);
+ break;
+ case 40:
+ case 38:
+ return this.use_arrow_key(keyCode, mod_key, msg_list_frame);
+ break;
+ case 46:
+ return this.use_delete_key(keyCode, mod_key, msg_list_frame);
+ break;
+ default:
+ return true;
+ }
+ return true;
+ }
+
+ this.use_arrow_key = function(keyCode, mod_key, msg_list_frame) {
+ var scroll_to = 0;
+ if (keyCode == 40) { // down arrow key pressed
+ new_row = this.get_next_row();
+ if (!new_row) return false;
+ scroll_to = (Number(new_row.offsetTop) + Number(new_row.offsetHeight)) - Number(msg_list_frame.offsetHeight);
+ } else if (keyCode == 38) { // up arrow key pressed
+ new_row = this.get_prev_row();
+ if (!new_row) return false;
+ scroll_to = new_row.offsetTop;
+ } else {return true;}
+
+ this.select_row(new_row.uid,mod_key,true);
+
+ if (((Number(new_row.offsetTop)) < (Number(msg_list_frame.scrollTop))) ||
+ ((Number(new_row.offsetTop) + Number(new_row.offsetHeight)) > (Number(msg_list_frame.scrollTop) + Number(msg_list_frame.offsetHeight)))) {
+ msg_list_frame.scrollTop = scroll_to;
+ }
+ return false;
+ };
+
+ this.use_delete_key = function(keyCode, mod_key, msg_list_frame){
+ this.command('delete','',this);
+ return false;
+ }
+
+ // get all message rows from HTML table and init each row
+ this.init_messagelist = function(msg_list)
+ {
+ if (msg_list && msg_list.tBodies[0])
+ {
+
+ this.message_rows = new Array();
+
+ var row;
+ for(var r=0; r<msg_list.tBodies[0].childNodes.length; r++)
+ {
+ row = msg_list.tBodies[0].childNodes[r];
+ while (row && (row.nodeType != 1 || row.style.display == 'none')) {
+ row = row.nextSibling;
+ r++;
+ }
+ //row = msg_list.tBodies[0].rows[r];
+ if (row) this.init_message_row(row);
+ }
+ }
+
+ // alias to common rows array
+ this.list_rows = this.message_rows;
+ };
+
+
+ // make references in internal array and set event handlers
+ this.init_message_row = function(row)
+ {
+ var uid, msg_icon;
+
+ if (String(row.id).match(/rcmrow([0-9]+)/))
+ {
+ uid = RegExp.$1;
+ row.uid = uid;
+
+ this.message_rows[uid] = {id:row.id, obj:row,
+ classname:row.className,
+ deleted:this.env.messages[uid] ? this.env.messages[uid].deleted : null,
+ unread:this.env.messages[uid] ? this.env.messages[uid].unread : null,
+ replied:this.env.messages[uid] ? this.env.messages[uid].replied : null};
+
+ // set eventhandlers to table row
+ row.onmousedown = function(e){ return rcube_webmail_client.drag_row(e, this.uid); };
+ row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.uid); };
+
+ if (document.all)
+ row.onselectstart = function() { return false; };
+
+ // set eventhandler to message icon
+ if ((msg_icon = row.cells[0].childNodes[0]) && row.cells[0].childNodes[0].nodeName=='IMG')
+ {
+ msg_icon.id = 'msgicn_'+uid;
+ msg_icon._row = row;
+ msg_icon.onmousedown = function(e) { rcube_webmail_client.command('toggle_status', this); };
+
+ // get message icon and save original icon src
+ this.message_rows[uid].icon = msg_icon;
+ }
+ }
+ };
+
+
+ // init message compose form: set focus and eventhandlers
+ this.init_messageform = function()
+ {
+ if (!this.gui_objects.messageform)
+ return false;
+
+ //this.messageform = this.gui_objects.messageform;
+ var input_from = rcube_find_object('_from');
+ var input_to = rcube_find_object('_to');
+ var input_cc = rcube_find_object('_cc');
+ var input_bcc = rcube_find_object('_bcc');
+ var input_replyto = rcube_find_object('_replyto');
+ var input_subject = rcube_find_object('_subject');
+ var input_message = rcube_find_object('_message');
+
+ // init live search events
+ if (input_to)
+ this.init_address_input_events(input_to);
+ if (input_cc)
+ this.init_address_input_events(input_cc);
+ if (input_bcc)
+ this.init_address_input_events(input_bcc);
+
+ // add signature according to selected identity
+ if (input_from && input_from.type=='select-one')
+ this.change_identity(input_from);
+
+ if (input_to && input_to.value=='')
+ input_to.focus();
+ else if (input_subject && input_subject.value=='')
+ input_subject.focus();
+ else if (input_message)
+ this.set_caret2start(input_message); // input_message.focus();
+
+ // get summary of all field values
+ this.cmp_hash = this.compose_field_hash();
+
+ // start the auto-save timer
+ this.auto_save_start();
+ };
+
+ this.init_address_input_events = function(obj)
+ {
+ var handler = function(e){ return rcube_webmail_client.ksearch_keypress(e,this); };
+ var handler2 = function(e){ return rcube_webmail_client.ksearch_blur(e,this); };
+
+ if (bw.safari)
+ {
+ obj.addEventListener('keydown', handler, false);
+ // obj.addEventListener('blur', handler2, false);
+ }
+ else if (bw.mz)
+ {
+ obj.addEventListener('keypress', handler, false);
+ obj.addEventListener('blur', handler2, false);
+ }
+ else if (bw.ie)
+ {
+ obj.onkeydown = handler;
+ //obj.attachEvent('onkeydown', handler);
+ // obj.attachEvent('onblur', handler2, false);
+ }
+
+ obj.setAttribute('autocomplete', 'off');
+ };
+
+
+
+ // get all contact rows from HTML table and init each row
+ this.init_contactslist = function(contacts_list)
+ {
+ if (contacts_list && contacts_list.tBodies[0])
+ {
+ this.contact_rows = new Array();
+
+ var row;
+ for(var r=0; r<contacts_list.tBodies[0].childNodes.length; r++)
+ {
+ row = contacts_list.tBodies[0].childNodes[r];
+ this.init_table_row(row, 'contact_rows');
+ }
+ }
+
+ // alias to common rows array
+ this.list_rows = this.contact_rows;
+
+ if (this.env.cid)
+ this.highlight_row(this.env.cid);
+ };
+
+
+ // get all contact rows from HTML table and init each row
+ this.init_ldapsearchlist = function(ldap_contacts_list)
+ {
+ if (ldap_contacts_list && ldap_contacts_list.tBodies[0])
+ {
+ this.ldap_contact_rows = new Array();
+
+ var row;
+ for(var r=0; r<ldap_contacts_list.tBodies[0].childNodes.length; r++)
+ {
+ row = ldap_contacts_list.tBodies[0].childNodes[r];
+ this.init_table_row(row, 'ldap_contact_rows');
+ }
+ }
+
+ // alias to common rows array
+ this.list_rows = this.ldap_contact_rows;
+ };
+
+
+ // make references in internal array and set event handlers
+ this.init_table_row = function(row, array_name)
+ {
+ var cid;
+
+ if (String(row.id).match(/rcmrow([0-9]+)/))
+ {
+ cid = RegExp.$1;
+ row.cid = cid;
+
+ this[array_name][cid] = {id:row.id,
+ obj:row,
+ classname:row.className};
+
+ // set eventhandlers to table row
+ row.onmousedown = function(e) { rcube_webmail_client.in_selection_before=this.cid; return false; }; // fake for drag handler
+ row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.cid); };
+ }
+ };
+
+
+ // get all contact rows from HTML table and init each row
+ this.init_identitieslist = function(identities_list)
+ {
+ if (identities_list && identities_list.tBodies[0])
+ {
+ this.identity_rows = new Array();
+
+ var row;
+ for(var r=0; r<identities_list.tBodies[0].childNodes.length; r++)
+ {
+ row = identities_list.tBodies[0].childNodes[r];
+ this.init_table_row(row, 'identity_rows');
+ }
+ }
+
+ // alias to common rows array
+ this.list_rows = this.identity_rows;
+
+ if (this.env.iid)
+ this.highlight_row(this.env.iid);
+ };
+
+
+
+ /*********************************************************/
+ /********* client command interface *********/
+ /*********************************************************/
+
+
+ // execute a specific command on the web client
+ this.command = function(command, props, obj)
+ {
+ if (obj && obj.blur)
+ obj.blur();
+
+ if (this.busy)
+ return false;
+
+ // command not supported or allowed
+ if (!this.commands[command])
+ {
+ // pass command to parent window
+ if (this.env.framed && parent.rcmail && parent.rcmail.command)
+ parent.rcmail.command(command, props);
+
+ return false;
+ }
+
+
+ // check input before leaving compose step
+ if (this.task=='mail' && this.env.action=='compose' && (command=='list' || command=='mail' || command=='addressbook' || command=='settings'))
+ {
+ if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
+ return false;
+ }
+
+
+ // process command
+ switch (command)
+ {
+ case 'login':
+ if (this.gui_objects.loginform)
+ this.gui_objects.loginform.submit();
+ break;
+
+ case 'logout':
+ location.href = this.env.comm_path+'&_action=logout';
+ break;
+
+ // commands to switch task
+ case 'mail':
+ case 'addressbook':
+ case 'settings':
+ this.switch_task(command);
+ break;
+
+
+ // misc list commands
+ case 'list':
+ if (this.task=='mail')
+ {
+ if (this.env.search_request<0 || (this.env.search_request && props != this.env.mailbox))
+ this.reset_qsearch();
+
+ // Reset message list header, unless returning from compose/read/etc
+ // don't know what this is good for (thomasb, 2006/07/25)
+ //if (this.env.mailbox != props && this.message_rows)
+ // this.clear_message_list_header();
+
+ this.list_mailbox(props);
+ }
+ else if (this.task=='addressbook')
+ this.list_contacts();
+ break;
+
+
+ case 'sort':
+ // get the type of sorting
+ var a_sort = props.split('_');
+ var sort_col = a_sort[0];
+ var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
+ var header;
+
+ // no sort order specified: toggle
+ if (sort_order==null)
+ {
+ if (this.env.sort_col==sort_col)
+ sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC';
+ else
+ sort_order = this.env.sort_order;
+ }
+
+ if (this.env.sort_col==sort_col && this.env.sort_order==sort_order)
+ break;
+
+ // set table header class
+ if (header = document.getElementById('rcmHead'+this.env.sort_col))
+ this.set_classname(header, 'sorted'+(this.env.sort_order.toUpperCase()), false);
+ if (header = document.getElementById('rcmHead'+sort_col))
+ this.set_classname(header, 'sorted'+sort_order, true);
+
+ // save new sort properties
+ this.env.sort_col = sort_col;
+ this.env.sort_order = sort_order;
+
+ // reload message list
+ this.list_mailbox('', '', sort_col+'_'+sort_order);
+ break;
+
+ case 'nextpage':
+ this.list_page('next');
+ break;
+
+ case 'previouspage':
+ this.list_page('prev');
+ break;
+
+ case 'expunge':
+ if (this.env.messagecount)
+ this.expunge_mailbox(this.env.mailbox);
+ break;
+
+ case 'purge':
+ case 'empty-mailbox':
+ if (this.env.messagecount)
+ this.purge_mailbox(this.env.mailbox);
+ break;
+
+
+ // common commands used in multiple tasks
+ case 'show':
+ if (this.task=='mail')
+ {
+ var uid = this.get_single_uid();
+ if (uid && (!this.env.uid || uid != this.env.uid))
+ {
+ if (this.env.mailbox==this.env.drafts_mailbox)
+ {
+ this.set_busy(true);
+ location.href = this.env.comm_path+'&_action=compose&_draft_uid='+uid+'&_mbox='+escape(this.env.mailbox);
+ }
+ else
+ this.show_message(uid);
+ }
+ }
+ else if (this.task=='addressbook')
+ {
+ var cid = props ? props : this.get_single_cid();
+ if (cid && !(this.env.action=='show' && cid==this.env.cid))
+ this.load_contact(cid, 'show');
+ }
+ break;
+
+ case 'add':
+ if (this.task=='addressbook')
+ if (!window.frames[this.env.contentframe].rcmail)
+ this.load_contact(0, 'add');
+ else
+ {
+ if (window.frames[this.env.contentframe].rcmail.selection.length)
+ this.add_ldap_contacts();
+ else
+ this.load_contact(0, 'add');
+ }
+ else if (this.task=='settings')
+ {
+ this.clear_selection();
+ this.load_identity(0, 'add-identity');
+ }
+ break;
+
+ case 'edit':
+ var cid;
+ if (this.task=='addressbook' && (cid = this.get_single_cid()))
+ this.load_contact(cid, 'edit');
+ else if (this.task=='settings' && props)
+ this.load_identity(props, 'edit-identity');
+ break;
+
+ case 'save-identity':
+ case 'save':
+ if (this.gui_objects.editform)
+ {
+ var input_pagesize = rcube_find_object('_pagesize');
+ var input_name = rcube_find_object('_name');
+ var input_email = rcube_find_object('_email');
+
+ // user prefs
+ if (input_pagesize && isNaN(input_pagesize.value))
+ {
+ alert(this.get_label('nopagesizewarning'));
+ input_pagesize.focus();
+ break;
+ }
+ // contacts/identities
+ else
+ {
+ if (input_name && input_name.value == '')
+ {
+ alert(this.get_label('nonamewarning'));
+ input_name.focus();
+ break;
+ }
+ else if (input_email && !rcube_check_email(input_email.value))
+ {
+ alert(this.get_label('noemailwarning'));
+ input_email.focus();
+ break;
+ }
+ }
+
+ this.gui_objects.editform.submit();
+ }
+ break;
+
+ case 'delete':
+ // mail task
+ if (this.task=='mail')
+ this.delete_messages();
+ // addressbook task
+ else if (this.task=='addressbook')
+ this.delete_contacts();
+ // user settings task
+ else if (this.task=='settings')
+ this.delete_identity();
+ break;
+
+
+ // mail task commands
+ case 'move':
+ case 'moveto':
+ this.move_messages(props);
+ break;
+
+ case 'toggle_status':
+ if (props && !props._row)
+ break;
+
+ var uid;
+ var flag = 'read';
+
+ if (props._row.uid)
+ {
+ uid = props._row.uid;
+ this.dont_select = true;
+ // toggle read/unread
+ if (this.message_rows[uid].deleted) {
+ flag = 'undelete';
+ } else if (!this.message_rows[uid].unread)
+ flag = 'unread';
+ }
+
+ this.mark_message(flag, uid);
+ break;
+
+ case 'load-images':
+ if (this.env.uid)
+ this.show_message(this.env.uid, true);
+ break;
+
+ case 'load-attachment':
+ var url = this.env.comm_path+'&_action=get&_mbox='+this.env.mailbox+'&_uid='+this.env.uid+'&_part='+props.part;
+
+ // open attachment in frame if it's of a supported mimetype
+ if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0)
+ {
+ this.attachment_win = window.open(url+'&_frame=1', 'rcubemailattachment');
+ if (this.attachment_win)
+ {
+ setTimeout(this.ref+'.attachment_win.focus()', 10);
+ break;
+ }
+ }
+
+ location.href = url;
+ break;
+
+ case 'select-all':
+ this.select_all(props);
+ break;
+
+ case 'select-none':
+ this.clear_selection();
+ break;
+
+ case 'nextmessage':
+ if (this.env.next_uid)
+ this.show_message(this.env.next_uid);
+ //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.next_uid+'&_mbox='+this.env.mailbox;
+ break;
+
+ case 'previousmessage':
+ if (this.env.prev_uid)
+ this.show_message(this.env.prev_uid);
+ //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.prev_uid+'&_mbox='+this.env.mailbox;
+ break;
+
+ case 'checkmail':
+ this.check_for_recent();
+ break;
+
+ case 'compose':
+ var url = this.env.comm_path+'&_action=compose';
+
+ if (this.task=='mail' && this.env.mailbox==this.env.drafts_mailbox)
+ {
+ var uid;
+ if (uid = this.get_single_uid())
+ url += '&_draft_uid='+uid+'&_mbox='+escape(this.env.mailbox);
+ }
+ // modify url if we're in addressbook
+ else if (this.task=='addressbook')
+ {
+ url = this.get_task_url('mail', url);
+ var a_cids = new Array();
+
+ // use contact_id passed as command parameter
+ if (props)
+ a_cids[a_cids.length] = props;
+
+ // get selected contacts
+ else
+ {
+ if (!window.frames[this.env.contentframe].rcmail.selection.length)
+ {
+ for (var n=0; n<this.selection.length; n++)
+ a_cids[a_cids.length] = this.selection[n];
+ }
+ else
+ {
+ var frameRcmail = window.frames[this.env.contentframe].rcmail;
+ // get the email address(es)
+ for (var n=0; n<frameRcmail.selection.length; n++)
+ a_cids[a_cids.length] = frameRcmail.ldap_contact_rows[frameRcmail.selection[n]].obj.cells[1].innerHTML;
+ }
+ }
+ if (a_cids.length)
+ url += '&_to='+a_cids.join(',');
+ else
+ break;
+
+ }
+ else if (props)
+ url += '&_to='+encodeURIComponent(props);
+
+ // don't know if this is necessary...
+ url = url.replace(/&_framed=1/, "");
+
+ this.set_busy(true);
+
+ // need parent in case we are coming from the contact frame
+ if (this.env.framed)
+ parent.location.href = url;
+ else
+ location.href = url;
+ break;
+
+ case 'spellcheck':
+ if (this.env.spellcheck && this.env.spellcheck.spellCheck)
+ this.env.spellcheck.spellCheck(this.env.spellcheck.check_link);
+ break;
+
+ case 'savedraft':
+ // Reset the auto-save timer
+ self.clearTimeout(this.save_timer);
+
+ if (!this.gui_objects.messageform)
+ break;
+
+ // if saving Drafts is disabled in main.inc.php
+ if (!this.env.drafts_mailbox)
+ break;
+
+ this.set_busy(true, 'savingmessage');
+ var form = this.gui_objects.messageform;
+ form.target = "savetarget";
+ form.submit();
+ break;
+
+ case 'send':
+ if (!this.gui_objects.messageform)
+ break;
+
+ if (!this.check_compose_input())
+ break;
+
+ // Reset the auto-save timer
+ self.clearTimeout(this.save_timer);
+
+ // all checks passed, send message
+ this.set_busy(true, 'sendingmessage');
+ var form = this.gui_objects.messageform;
+ form.target = "savetarget";
+ form._draft.value = '';
+ form.submit();
+
+ // clear timeout (sending could take longer)
+ clearTimeout(this.request_timer);
+ break;
+
+ case 'add-attachment':
+ this.show_attachment_form(true);
+
+ case 'send-attachment':
+ // Reset the auto-save timer
+ self.clearTimeout(this.save_timer);
+
+ this.upload_file(props)
+ break;
+
+ case 'remove-attachment':
+ this.remove_attachment(props);
+ break;
+
+ case 'reply-all':
+ case 'reply':
+ var uid;
+ if (uid = this.get_single_uid())
+ {
+ this.set_busy(true);
+ location.href = this.env.comm_path+'&_action=compose&_reply_uid='+uid+'&_mbox='+escape(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : '');
+ }
+ break;
+
+ case 'forward':
+ var uid;
+ if (uid = this.get_single_uid())
+ {
+ this.set_busy(true);
+ location.href = this.env.comm_path+'&_action=compose&_forward_uid='+uid+'&_mbox='+escape(this.env.mailbox);
+ }
+ break;
+
+ case 'print':
+ var uid;
+ if (uid = this.get_single_uid())
+ {
+ this.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+escape(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''));
+ if (this.printwin)
+ setTimeout(this.ref+'.printwin.focus()', 20);
+ }
+ break;
+
+ case 'viewsource':
+ var uid;
+ if (uid = this.get_single_uid())
+ {
+ this.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+this.env.uid+'&_mbox='+escape(this.env.mailbox));
+ if (this.sourcewin)
+ setTimeout(this.ref+'.sourcewin.focus()', 20);
+ }
+ break;
+
+ case 'add-contact':
+ this.add_contact(props);
+ break;
+
+ // mail quicksearch
+ case 'search':
+ if (!props && this.gui_objects.qsearchbox)
+ props = this.gui_objects.qsearchbox.value;
+ if (props)
+ this.qsearch(escape(props), this.env.mailbox);
+ break;
+
+ // reset quicksearch
+ case 'reset-search':
+ var s = this.env.search_request;
+ this.reset_qsearch();
+
+ if (s)
+ this.list_mailbox(this.env.mailbox);
+ break;
+
+ // ldap search
+ case 'ldappublicsearch':
+ if (this.gui_objects.ldappublicsearchform)
+ this.gui_objects.ldappublicsearchform.submit();
+ else
+ this.ldappublicsearch(command);
+ break;
+
+
+ // user settings commands
+ case 'preferences':
+ location.href = this.env.comm_path;
+ break;
+
+ case 'identities':
+ location.href = this.env.comm_path+'&_action=identities';
+ break;
+
+ case 'delete-identity':
+ this.delete_identity();
+
+ case 'folders':
+ location.href = this.env.comm_path+'&_action=folders';
+ break;
+
+ case 'subscribe':
+ this.subscribe_folder(props);
+ break;
+
+ case 'unsubscribe':
+ this.unsubscribe_folder(props);
+ break;
+
+ case 'create-folder':
+ this.create_folder(props);
+ break;
+
+ case 'rename-folder':
+ this.rename_folder(props);
+ break;
+
+ case 'delete-folder':
+ if (confirm(this.get_label('deletefolderconfirm')))
+ this.delete_folder(props);
+ break;
+
+ }
+
+ return obj ? false : true;
+ };
+
+
+ // set command enabled or disabled
+ this.enable_command = function()
+ {
+ var args = arguments;
+ if(!args.length) return -1;
+
+ var command;
+ var enable = args[args.length-1];
+
+ for(var n=0; n<args.length-1; n++)
+ {
+ command = args[n];
+ this.commands[command] = enable;
+ this.set_button(command, (enable ? 'act' : 'pas'));
+ }
+ return true;
+ };
+
+
+ // lock/unlock interface
+ this.set_busy = function(a, message)
+ {
+ if (a && message)
+ {
+ var msg = this.get_label(message);
+ if (msg==message)
+ msg = 'Loading...';
+
+ this.display_message(msg, 'loading', true);
+ }
+ else if (!a && this.busy)
+ this.hide_message();
+
+ this.busy = a;
+ //document.body.style.cursor = a ? 'wait' : 'default';
+
+ if (this.gui_objects.editform)
+ this.lock_form(this.gui_objects.editform, a);
+
+ // clear pending timer
+ if (this.request_timer)
+ clearTimeout(this.request_timer);
+
+ // set timer for requests
+ if (a && this.env.request_timeout)
+ this.request_timer = setTimeout(this.ref+'.request_timed_out()', this.env.request_timeout * 1000);
+ };
+
+
+ // return a localized string
+ this.get_label = function(name)
+ {
+ if (this.labels[name])
+ return this.labels[name];
+ else
+ return name;
+ };
+
+
+ // switch to another application task
+ this.switch_task = function(task)
+ {
+ if (this.task===task && task!='mail')
+ return;
+
+ var url = this.get_task_url(task);
+ if (task=='mail')
+ url += '&_mbox=INBOX';
+
+ this.set_busy(true);
+ location.href = url;
+ };
+
+
+ this.get_task_url = function(task, url)
+ {
+ if (!url)
+ url = this.env.comm_path;
+
+ return url.replace(/_task=[a-z]+/, '_task='+task);
+ };
+
+
+ // called when a request timed out
+ this.request_timed_out = function()
+ {
+ this.set_busy(false);
+ this.display_message('Request timed out!', 'error');
+ };
+
+
+ /*********************************************************/
+ /********* event handling methods *********/
+ /*********************************************************/
+
+
+ // onmouseup handler for mailboxlist item
+ this.mbox_mouse_up = function(mbox)
+ {
+ if (this.drag_active)
+ {
+ this.unfocus_mailbox(mbox);
+ this.command('moveto', mbox);
+ }
+ else
+ this.command('list', mbox);
+
+ return false;
+ };
+
+
+ // onmousedown-handler of message list row
+ this.drag_row = function(e, id)
+ {
+ this.in_selection_before = this.in_selection(id) ? id : false;
+
+ // don't do anything (another action processed before)
+ if (this.dont_select)
+ return false;
+
+ // selects currently unselected row
+ if (!this.in_selection_before && !this.list_rows[id].clicked)
+ {
+ var mod_key = this.get_modifier(e);
+ this.select_row(id,mod_key,false);
+ }
+
+ if (this.selection.length)
+ {
+ this.drag_start = true;
+ document.onmousemove = function(e){ return rcube_webmail_client.drag_mouse_move(e); };
+ document.onmouseup = function(e){ return rcube_webmail_client.drag_mouse_up(e); };
+ }
+
+ return false;
+ };
+
+
+ // onmouseup-handler of message list row
+ this.click_row = function(e, id)
+ {
+ var mod_key = this.get_modifier(e);
+
+ // don't do anything (another action processed before)
+ if (this.dont_select)
+ {
+ this.dont_select = false;
+ return false;
+ }
+
+ // unselects currently selected row
+ if (!this.drag_active && this.in_selection_before==id && !this.list_rows[id].clicked)
+ this.select_row(id,mod_key,false);
+
+ this.drag_start = false;
+ this.in_selection_before = false;
+
+ // row was double clicked
+ if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && this.in_selection(id))
+ {
+ if (this.env.mailbox==this.env.drafts_mailbox)
+ {
+ this.set_busy(true);
+ location.href = this.env.comm_path+'&_action=compose&_draft_uid='+id+'&_mbox='+escape(this.env.mailbox);
+ }
+ else
+ {
+ this.show_message(id);
+ }
+ return false;
+ }
+ else if (this.task=='addressbook')
+ {
+ if (this.contact_rows && this.selection.length==1)
+ {
+ this.load_contact(this.selection[0], 'show', true);
+ // change the text for the add contact button
+ var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A');
+ for (i = 0; i < links.length; i++)
+ {
+ var onclickstring = new String(links[i].onclick);
+ if (onclickstring.search('\"add\"') != -1)
+ links[i].title = this.env.newcontact;
+ }
+ }
+ else if (this.contact_rows && this.contact_rows[id].clicked)
+ {
+ this.load_contact(id, 'show');
+ return false;
+ }
+ else if (this.ldap_contact_rows && !this.ldap_contact_rows[id].clicked)
+ {
+ // clear selection
+ parent.rcmail.clear_selection();
+
+ // disable delete
+ parent.rcmail.set_button('delete', 'pas');
+
+ // change the text for the add contact button
+ var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A');
+ for (i = 0; i < links.length; i++)
+ {
+ var onclickstring = new String(links[i].onclick);
+ if (onclickstring.search('\"add\"') != -1)
+ links[i].title = this.env.addcontact;
+ }
+ }
+ // handle double click event
+ else if (this.ldap_contact_rows && this.selection.length==1 && this.ldap_contact_rows[id].clicked)
+ this.command('compose', this.ldap_contact_rows[id].obj.cells[1].innerHTML);
+ else if (this.env.contentframe)
+ {
+ var elm = document.getElementById(this.env.contentframe);
+ elm.style.visibility = 'hidden';
+ }
+ }
+ else if (this.task=='settings')
+ {
+ if (this.selection.length==1)
+ this.command('edit', this.selection[0]);
+ }
+
+ this.list_rows[id].clicked = true;
+ setTimeout(this.ref+'.list_rows['+id+'].clicked=false;', this.dblclick_time);
+
+ return false;
+ };
+
+
+
+ /*********************************************************/
+ /********* (message) list functionality *********/
+ /*********************************************************/
+
+ // get next and previous rows that are not hidden
+ this.get_next_row = function(){
+ if (!this.list_rows) return false;
+ var last_selected_row = this.list_rows[this.last_selected];
+ var new_row = last_selected_row.obj.nextSibling;
+ while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) {
+ new_row = new_row.nextSibling;
+ }
+ return new_row;
+ }
+
+ this.get_prev_row = function(){
+ if (!this.list_rows) return false;
+ var last_selected_row = this.list_rows[this.last_selected];
+ var new_row = last_selected_row.obj.previousSibling;
+ while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) {
+ new_row = new_row.previousSibling;
+ }
+ return new_row;
+ }
+
+ // highlight/unhighlight a row
+ this.highlight_row = function(id, multiple)
+ {
+ var selected = false
+
+ if (this.list_rows[id] && !multiple)
+ {
+ this.clear_selection();
+ this.selection[0] = id;
+ this.list_rows[id].obj.className += ' selected';
+ selected = true;
+ }
+
+ else if (this.list_rows[id])
+ {
+ if (!this.in_selection(id)) // select row
+ {
+ this.selection[this.selection.length] = id;
+ this.set_classname(this.list_rows[id].obj, 'selected', true);
+ }
+ else // unselect row
+ {
+ var p = find_in_array(id, this.selection);
+ var a_pre = this.selection.slice(0, p);
+ var a_post = this.selection.slice(p+1, this.selection.length);
+ this.selection = a_pre.concat(a_post);
+ this.set_classname(this.list_rows[id].obj, 'selected', false);
+ this.set_classname(this.list_rows[id].obj, 'unfocused', false);
+ }
+ selected = (this.selection.length==1);
+ }
+
+ // enable/disable commands for message
+ if (this.task=='mail')
+ {
+ if (this.env.mailbox==this.env.drafts_mailbox)
+ {
+ this.enable_command('show', selected);
+ this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false);
+ }
+ else
+ {
+ this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected);
+ this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false);
+ }
+ }
+ else if (this.task=='addressbook')
+ {
+ this.enable_command('edit', /*'print',*/ selected);
+ this.enable_command('delete', 'compose', this.selection.length>0 ? true : false);
+ }
+ };
+
+
+// selects or unselects the proper row depending on the modifier key pressed
+ this.select_row = function(id,mod_key,with_mouse) {
+ if (!mod_key) {
+ this.shift_start = id;
+ this.highlight_row(id, false);
+ } else {
+ switch (mod_key) {
+ case SHIFT_KEY: {
+ this.shift_select(id,false);
+ break; }
+ case CONTROL_KEY: {
+ this.shift_start = id;
+ if (!with_mouse)
+ this.highlight_row(id, true);
+ break;
+ }
+ case CONTROL_SHIFT_KEY: {
+ this.shift_select(id,true);
+ break;
+ }
+ default: {
+ this.highlight_row(id, false);
+ break;
+ }
+ }
+ }
+ if (this.last_selected != 0 && this.list_rows[this.last_selected])
+ this.set_classname(this.list_rows[this.last_selected].obj, 'focused', false);
+
+ this.last_selected = id;
+ this.set_classname(this.list_rows[id].obj, 'focused', true);
+ };
+
+ this.shift_select = function(id, control) {
+ var from_rowIndex = this.list_rows[this.shift_start].obj.rowIndex;
+ var to_rowIndex = this.list_rows[id].obj.rowIndex;
+
+ var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex);
+ var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex);
+
+ // iterate through the entire message list
+ for (var n in this.list_rows) {
+ if ((this.list_rows[n].obj.rowIndex >= i) && (this.list_rows[n].obj.rowIndex <= j)) {
+ if (!this.in_selection(n))
+ this.highlight_row(n, true);
+ } else {
+ if (this.in_selection(n) && !control)
+ this.highlight_row(n, true);
+ }
+ }
+ };
+
+
+ this.clear_selection = function()
+ {
+ for(var n=0; n<this.selection.length; n++)
+ if (this.list_rows[this.selection[n]]) {
+ this.set_classname(this.list_rows[this.selection[n]].obj, 'selected', false);
+ this.set_classname(this.list_rows[this.selection[n]].obj, 'unfocused', false);
+ }
+ this.selection = new Array();
+ };
+
+
+ // check if given id is part of the current selection
+ this.in_selection = function(id)
+ {
+ for(var n in this.selection)
+ if (this.selection[n]==id)
+ return true;
+
+ return false;
+ };
+
+
+ // select each row in list
+ this.select_all = function(filter)
+ {
+ if (!this.list_rows || !this.list_rows.length)
+ return false;
+
+ // reset selection first
+ this.clear_selection();
+
+ for (var n in this.list_rows)
+ {
+ if (!filter || this.list_rows[n][filter]==true)
+ {
+ this.last_selected = n;
+ this.highlight_row(n, true);
+ }
+ }
+
+ return true;
+ };
+
+
+ // when user doble-clicks on a row
+ this.show_message = function(id, safe)
+ {
+ var add_url = '';
+ var target = window;
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+ {
+ target = window.frames[this.env.contentframe];
+ add_url = '&_framed=1';
+ }
+
+ if (safe)
+ add_url = '&_safe=1';
+
+ if (id)
+ {
+ this.set_busy(true, 'loading');
+ target.location.href = this.env.comm_path+'&_action=show&_uid='+id+'&_mbox='+escape(this.env.mailbox)+add_url;
+ }
+ };
+
+
+
+ // list a specific page
+ this.list_page = function(page)
+ {
+ if (page=='next')
+ page = this.env.current_page+1;
+ if (page=='prev' && this.env.current_page>1)
+ page = this.env.current_page-1;
+
+ if (page > 0 && page <= this.env.pagecount)
+ {
+ this.env.current_page = page;
+
+ if (this.task=='mail')
+ this.list_mailbox(this.env.mailbox, page);
+ else if (this.task=='addressbook')
+ this.list_contacts(page);
+ }
+ };
+
+
+ // list messages of a specific mailbox
+ this.list_mailbox = function(mbox, page, sort)
+ {
+ this.last_selected = 0;
+ var add_url = '';
+ var target = window;
+
+ if (!mbox)
+ mbox = this.env.mailbox;
+
+ // add sort to url if set
+ if (sort)
+ add_url += '&_sort=' + sort;
+
+ // set page=1 if changeing to another mailbox
+ if (!page && mbox != this.env.mailbox)
+ {
+ page = 1;
+ add_url += '&_refresh=1';
+ this.env.current_page = page;
+ this.clear_selection();
+ }
+
+ // also send search request to get the right messages
+ if (this.env.search_request)
+ add_url += '&_search='+this.env.search_request;
+
+ this.select_mailbox(mbox);
+
+ // load message list remotely
+ if (this.gui_objects.messagelist)
+ {
+ this.list_mailbox_remote(mbox, page, add_url);
+ return;
+ }
+
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+ {
+ target = window.frames[this.env.contentframe];
+ add_url += '&_framed=1';
+ }
+
+ // load message list to target frame/window
+ if (mbox)
+ {
+ this.set_busy(true, 'loading');
+ target.location.href = this.env.comm_path+'&_mbox='+escape(mbox)+(page ? '&_page='+page : '')+add_url;
+ }
+ };
+
+
+ // send remote request to load message list
+ this.list_mailbox_remote = function(mbox, page, add_url)
+ {
+ // clear message list first
+ this.clear_message_list();
+
+ // send request to server
+ var url = '_mbox='+escape(mbox)+(page ? '&_page='+page : '');
+ this.set_busy(true, 'loading');
+ this.http_request('list', url+add_url, true);
+ };
+
+
+ this.clear_message_list = function()
+ {
+ var table = this.gui_objects.messagelist;
+
+ var tbody = document.createElement('TBODY');
+ table.insertBefore(tbody, table.tBodies[0]);
+ table.removeChild(table.tBodies[1]);
+
+ this.message_rows = new Array();
+ this.list_rows = this.message_rows;
+
+ };
+
+
+ this.clear_message_list_header = function()
+ {
+ var table;
+ if (table = this.gui_objects.messagelist)
+ {
+ if (table.colgroup)
+ table.removeChild(table.colgroup);
+ if (table.tHead)
+ table.removeChild(table.tHead);
+
+ var colgroup = document.createElement('COLGROUP');
+ var thead = document.createElement('THEAD');
+ table.insertBefore(colgroup, table.tBodies[0]);
+ table.insertBefore(thead, table.tBodies[0]);
+ }
+ };
+
+
+ this.expunge_mailbox = function(mbox)
+ {
+ var lock = false;
+ var add_url = '';
+
+ // lock interface if it's the active mailbox
+ if (mbox == this.env.mailbox)
+ {
+ lock = true;
+ this.set_busy(true, 'loading');
+ add_url = '&_reload=1';
+ }
+
+ // send request to server
+ var url = '_mbox='+escape(mbox);
+ this.http_request('expunge', url+add_url, lock);
+ };
+
+
+ this.purge_mailbox = function(mbox)
+ {
+ var lock = false;
+ var add_url = '';
+
+ if (!confirm(this.get_label('purgefolderconfirm')))
+ return false;
+
+ // lock interface if it's the active mailbox
+ if (mbox == this.env.mailbox)
+ {
+ lock = true;
+ this.set_busy(true, 'loading');
+ add_url = '&_reload=1';
+ }
+
+ // send request to server
+ var url = '_mbox='+escape(mbox);
+ this.http_request('purge', url+add_url, lock);
+ return true;
+ };
+
+ this.focus_mailbox = function(mbox)
+ {
+ var mbox_li;
+ if (this.drag_active && mbox != this.env.mailbox && (mbox_li = this.get_mailbox_li(mbox)))
+ this.set_classname(mbox_li, 'droptarget', true);
+ }
+
+ this.unfocus_mailbox = function(mbox)
+ {
+ var mbox_li;
+ if (this.drag_active && (mbox_li = this.get_mailbox_li(mbox)))
+ this.set_classname(mbox_li, 'droptarget', false);
+ }
+
+ // move selected messages to the specified mailbox
+ this.move_messages = function(mbox)
+ {
+ // exit if no mailbox specified or if selection is empty
+ if (!mbox || !(this.selection.length || this.env.uid) || mbox==this.env.mailbox)
+ return;
+
+ var a_uids = new Array();
+
+ if (this.env.uid)
+ a_uids[a_uids.length] = this.env.uid;
+ else
+ {
+ var id;
+ for (var n=0; n<this.selection.length; n++)
+ {
+ id = this.selection[n];
+ a_uids[a_uids.length] = id;
+
+ // 'remove' message row from list (just hide it)
+ if (this.message_rows[id].obj)
+ this.message_rows[id].obj.style.display = 'none';
+ }
+ next_row = this.get_next_row();
+ prev_row = this.get_prev_row();
+ new_row = (next_row) ? next_row : prev_row;
+ if (new_row) this.select_row(new_row.uid,false,false);
+ }
+
+ var lock = false;
+
+ // show wait message
+ if (this.env.action=='show')
+ {
+ lock = true;
+ this.set_busy(true, 'movingmessage');
+ }
+ // send request to server
+ this.http_request('moveto', '_uid='+a_uids.join(',')+'&_mbox='+escape(this.env.mailbox)+'&_target_mbox='+escape(mbox)+'&_from='+(this.env.action ? this.env.action : ''), lock);
+ };
+
+ this.permanently_remove_messages = function() {
+ // exit if no mailbox specified or if selection is empty
+ if (!(this.selection.length || this.env.uid))
+ return;
+
+ var a_uids = new Array();
+
+ if (this.env.uid)
+ a_uids[a_uids.length] = this.env.uid;
+ else
+ {
+ var id;
+ for (var n=0; n<this.selection.length; n++)
+ {
+ id = this.selection[n];
+ a_uids[a_uids.length] = id;
+
+ // 'remove' message row from list (just hide it)
+ if (this.message_rows[id].obj)
+ this.message_rows[id].obj.style.display = 'none';
+ }
+ }
+ next_row = this.get_next_row();
+ prev_row = this.get_prev_row();
+ new_row = (next_row) ? next_row : prev_row;
+ if (new_row) this.select_row(new_row.uid,false,false);
+
+ // send request to server
+ this.http_request('delete', '_uid='+a_uids.join(',')+'&_mbox='+escape(this.env.mailbox)+'&_from='+(this.env.action ? this.env.action : ''));
+ }
+
+
+ // delete selected messages from the current mailbox
+ this.delete_messages = function()
+ {
+ // exit if no mailbox specified or if selection is empty
+ if (!(this.selection.length || this.env.uid))
+ return;
+ // if there is a trash mailbox defined and we're not currently in it:
+ if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase()!=String(this.env.trash_mailbox).toLowerCase())
+ this.move_messages(this.env.trash_mailbox);
+ // if there is a trash mailbox defined but we *are* in it:
+ else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase())
+ this.permanently_remove_messages();
+ // if there isn't a defined trash mailbox and the config is set to flag for deletion
+ else if (!this.env.trash_mailbox && this.env.flag_for_deletion) {
+ flag = 'delete';
+ this.mark_message(flag);
+ if(this.env.action=="show"){
+ this.command('nextmessage','',this);
+ } else if (this.selection.length == 1) {
+ next_row = this.get_next_row();
+ prev_row = this.get_prev_row();
+ new_row = (next_row) ? next_row : prev_row;
+ if (new_row) this.select_row(new_row.uid,false,false);
+ }
+ // if there isn't a defined trash mailbox and the config is set NOT to flag for deletion
+ }else if (!this.env.trash_mailbox && !this.env.flag_for_deletion) {
+ this.permanently_remove_messages();
+ }
+ return;
+ };
+
+
+ // set a specific flag to one or more messages
+ this.mark_message = function(flag, uid)
+ {
+ var a_uids = new Array();
+
+ if (uid)
+ a_uids[0] = uid;
+ else if (this.env.uid)
+ a_uids[0] = this.env.uid;
+ else
+ {
+ var id;
+ for (var n=0; n<this.selection.length; n++)
+ {
+ id = this.selection[n];
+ a_uids[a_uids.length] = id;
+ }
+ }
+ switch (flag) {
+ case 'read':
+ case 'unread':
+ this.toggle_read_status(flag,a_uids);
+ break;
+ case 'delete':
+ case 'undelete':
+ this.toggle_delete_status(a_uids);
+ break;
+ }
+ };
+
+ // set class to read/unread
+ this.toggle_read_status = function(flag, a_uids) {
+ // mark all message rows as read/unread
+ var icn_src;
+ for (var i=0; i<a_uids.length; i++)
+ {
+ uid = a_uids[i];
+ if (this.message_rows[uid])
+ {
+ this.message_rows[uid].unread = (flag=='unread' ? true : false);
+
+ if (this.message_rows[uid].classname.indexOf('unread')<0 && this.message_rows[uid].unread)
+ {
+ this.message_rows[uid].classname += ' unread';
+ this.set_classname(this.message_rows[uid].obj, 'unread', true);
+
+ if (this.env.unreadicon)
+ icn_src = this.env.unreadicon;
+ }
+ else if (!this.message_rows[uid].unread)
+ {
+ this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*unread/, '');
+ this.set_classname(this.message_rows[uid].obj, 'unread', false);
+
+ if (this.message_rows[uid].replied && this.env.repliedicon)
+ icn_src = this.env.repliedicon;
+ else if (this.env.messageicon)
+ icn_src = this.env.messageicon;
+ }
+
+ if (this.message_rows[uid].icon && icn_src)
+ this.message_rows[uid].icon.src = icn_src;
+ }
+ }
+ this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
+ }
+
+ // mark all message rows as deleted/undeleted
+ this.toggle_delete_status = function(a_uids) {
+ if (this.env.read_when_deleted) {
+ this.toggle_read_status('read',a_uids);
+ }
+ // if deleting message from "view message" don't bother with delete icon
+ if (this.env.action == "show")
+ return false;
+
+ if (a_uids.length==1){
+ if(this.message_rows[a_uids[0]].classname.indexOf('deleted') < 0 ){
+ this.flag_as_deleted(a_uids)
+ } else {
+ this.flag_as_undeleted(a_uids)
+ }
+ return true;
+ }
+
+ var all_deleted = true;
+
+ for (var i=0; i<a_uids.length; i++) {
+ uid = a_uids[i];
+ if (this.message_rows[uid]) {
+ if (this.message_rows[uid].classname.indexOf('deleted')<0) {
+ all_deleted = false;
+ break;
+ }
+ }
+ }
+
+ if (all_deleted)
+ this.flag_as_undeleted(a_uids);
+ else
+ this.flag_as_deleted(a_uids);
+
+ return true;
+ }
+
+ this.flag_as_undeleted = function(a_uids){
+ // if deleting message from "view message" don't bother with delete icon
+ if (this.env.action == "show")
+ return false;
+
+ var icn_src;
+
+ for (var i=0; i<a_uids.length; i++) {
+ uid = a_uids[i];
+ if (this.message_rows[uid]) {
+ this.message_rows[uid].deleted = false;
+
+ if (this.message_rows[uid].classname.indexOf('deleted') > 0) {
+ this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*deleted/, '');
+ this.set_classname(this.message_rows[uid].obj, 'deleted', false);
+ }
+ if (this.message_rows[uid].unread && this.env.unreadicon)
+ icn_src = this.env.unreadicon;
+ else if (this.message_rows[uid].replied && this.env.repliedicon)
+ icn_src = this.env.repliedicon;
+ else if (this.env.messageicon)
+ icn_src = this.env.messageicon;
+ if (this.message_rows[uid].icon && icn_src)
+ this.message_rows[uid].icon.src = icn_src;
+ }
+ }
+ this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=undelete');
+ return true;
+ }
+
+ this.flag_as_deleted = function(a_uids) {
+ // if deleting message from "view message" don't bother with delete icon
+ if (this.env.action == "show")
+ return false;
+
+ for (var i=0; i<a_uids.length; i++) {
+ uid = a_uids[i];
+ if (this.message_rows[uid]) {
+ this.message_rows[uid].deleted = true;
+
+ if (this.message_rows[uid].classname.indexOf('deleted')<0) {
+ this.message_rows[uid].classname += ' deleted';
+ this.set_classname(this.message_rows[uid].obj, 'deleted', true);
+ }
+ if (this.message_rows[uid].icon && this.env.deletedicon)
+ this.message_rows[uid].icon.src = this.env.deletedicon;
+ }
+ }
+ this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=delete');
+ return true;
+ }
+
+
+ this.get_mailbox_li = function(mbox)
+ {
+ if (this.gui_objects.mailboxlist)
+ {
+ mbox = String((mbox ? mbox : this.env.mailbox)).toLowerCase().replace(this.mbox_expression, '');
+ return document.getElementById('rcmbx'+mbox);
+ }
+
+ return null;
+ };
+
+
+ /*********************************************************/
+ /********* message compose methods *********/
+ /*********************************************************/
+
+
+ // checks the input fields before sending a message
+ this.check_compose_input = function()
+ {
+ // check input fields
+ var input_to = rcube_find_object('_to');
+ var input_subject = rcube_find_object('_subject');
+ var input_message = rcube_find_object('_message');
+
+ // check for empty recipient
+ if (input_to && !rcube_check_email(input_to.value, true))
+ {
+ alert(this.get_label('norecipientwarning'));
+ input_to.focus();
+ return false;
+ }
+
+ // display localized warning for missing subject
+ if (input_subject && input_subject.value == '')
+ {
+ var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
+
+ // user hit cancel, so don't send
+ if (!subject && subject !== '')
+ {
+ input_subject.focus();
+ return false;
+ }
+ else
+ {
+ input_subject.value = subject ? subject : this.get_label('nosubject');
+ }
+ }
+
+ // check for empty body
+ if (input_message.value=='')
+ {
+ if (!confirm(this.get_label('nobodywarning')))
+ {
+ input_message.focus();
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+
+ this.auto_save_start = function()
+ {
+ if (this.env.draft_autosave)
+ this.save_timer = self.setTimeout(this.ref+'.command("savedraft")', this.env.draft_autosave * 1000);
+ };
+
+
+ this.compose_field_hash = function()
+ {
+ // check input fields
+ var input_to = rcube_find_object('_to');
+ var input_cc = rcube_find_object('_to');
+ var input_bcc = rcube_find_object('_to');
+ var input_subject = rcube_find_object('_subject');
+ var input_message = rcube_find_object('_message');
+
+ var str = '';
+ if (input_to && input_to.value)
+ str += input_to.value+':';
+ if (input_cc && input_cc.value)
+ str += input_cc.value+':';
+ if (input_bcc && input_bcc.value)
+ str += input_bcc.value+':';
+ if (input_subject && input_subject.value)
+ str += input_subject.value+':';
+ if (input_message && input_message.value)
+ str += input_message.value;
+
+ return str;
+ };
+
+
+ this.change_identity = function(obj)
+ {
+ if (!obj || !obj.options)
+ return false;
+
+ var id = obj.options[obj.selectedIndex].value;
+ var input_message = rcube_find_object('_message');
+ var message = input_message ? input_message.value : '';
+ var sig, p;
+
+ if (!this.env.identity)
+ this.env.identity = id
+
+ // remove the 'old' signature
+ if (this.env.identity && this.env.signatures && this.env.signatures[this.env.identity])
+ {
+ sig = this.env.signatures[this.env.identity];
+ if (sig.indexOf('--')!=0)
+ sig = '--\n'+sig;
+
+ p = message.lastIndexOf(sig);
+ if (p>=0)
+ message = message.substring(0, p-1) + message.substring(p+sig.length, message.length);
+ }
+
+ // add the new signature string
+ if (this.env.signatures && this.env.signatures[id])
+ {
+ sig = this.env.signatures[id];
+ if (sig.indexOf('--')!=0)
+ sig = '--\n'+sig;
+ message += '\n'+sig;
+ }
+
+ if (input_message)
+ input_message.value = message;
+
+ this.env.identity = id;
+ return true;
+ };
+
+
+ this.show_attachment_form = function(a)
+ {
+ if (!this.gui_objects.uploadbox)
+ return false;
+
+ var elm, list;
+ if (elm = this.gui_objects.uploadbox)
+ {
+ if (a && (list = this.gui_objects.attachmentlist))
+ {
+ var pos = rcube_get_object_pos(list);
+ var left = pos.x;
+ var top = pos.y + list.offsetHeight + 10;
+
+ elm.style.top = top+'px';
+ elm.style.left = left+'px';
+ }
+
+ elm.style.visibility = a ? 'visible' : 'hidden';
+ }
+
+ // clear upload form
+ if (!a && this.gui_objects.attachmentform && this.gui_objects.attachmentform!=this.gui_objects.messageform)
+ this.gui_objects.attachmentform.reset();
+
+ return true;
+ };
+
+
+ // upload attachment file
+ this.upload_file = function(form)
+ {
+
+ if (!form)
+ return false;
+
+ // get file input fields
+ var send = false;
+ for (var n=0; n<form.elements.length; n++)
+ if (form.elements[n].type=='file' && form.elements[n].value)
+ {
+ send = true;
+ break;
+ }
+
+ // create hidden iframe and post upload form
+ if (send)
+ {
+ var ts = new Date().getTime();
+ var frame_name = 'rcmupload'+ts;
+
+ // have to do it this way for IE
+ // otherwise the form will be posted to a new window
+ if(document.all && !window.opera)
+ {
+ var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
+ document.body.insertAdjacentHTML('BeforeEnd',html);
+ }
+ else // for standards-compilant browsers
+ {
+ var frame = document.createElement('IFRAME');
+ frame.name = frame_name;
+ frame.width = 10;
+ frame.height = 10;
+ frame.style.visibility = 'hidden';
+ document.body.appendChild(frame);
+ }
+
+ form.target = frame_name;
+ form.action = this.env.comm_path+'&_action=upload';
+ form.setAttribute('enctype', 'multipart/form-data');
+ form.submit();
+ }
+
+ // set reference to the form object
+ this.gui_objects.attachmentform = form;
+ return true;
+ };
+
+
+ // add file name to attachment list
+ // called from upload page
+ this.add2attachment_list = function(name, content)
+ {
+ if (!this.gui_objects.attachmentlist)
+ return false;
+
+ var li = document.createElement('LI');
+ li.id = name;
+ li.innerHTML = content;
+ this.gui_objects.attachmentlist.appendChild(li);
+ return true;
+ };
+
+ this.remove_from_attachment_list = function(name)
+ {
+ if (!this.gui_objects.attachmentlist)
+ return false;
+
+ var list = this.gui_objects.attachmentlist.getElementsByTagName("li");
+ for (i=0;i<list.length;i++)
+ if (list[i].id == name)
+ this.gui_objects.attachmentlist.removeChild(list[i]);
+ };
+
+ this.remove_attachment = function(name)
+ {
+ if (name)
+ this.http_request('remove-attachment', '_file='+escape(name));
+
+ return true;
+ };
+
+ // send remote request to add a new contact
+ this.add_contact = function(value)
+ {
+ if (value)
+ this.http_request('addcontact', '_address='+value);
+
+ return true;
+ };
+
+ // send remote request to search mail
+ this.qsearch = function(value, mbox)
+ {
+ if (value && mbox)
+ {
+ this.clear_message_list();
+ this.set_busy(true, 'searching');
+ this.http_request('search', '_search='+value+'&_mbox='+mbox, true);
+ }
+ return true;
+ };
+
+ // reset quick-search form
+ this.reset_qsearch = function()
+ {
+ if (this.gui_objects.qsearchbox)
+ this.gui_objects.qsearchbox.value = '';
+
+ this.env.search_request = null;
+ return true;
+ };
+
+
+ this.sent_successfully = function(msg)
+ {
+ this.list_mailbox();
+ this.display_message(msg, 'confirmation', true);
+ }
+
+
+ /*********************************************************/
+ /********* keyboard live-search methods *********/
+ /*********************************************************/
+
+
+ // handler for keyboard events on address-fields
+ this.ksearch_keypress = function(e, obj)
+ {
+ if (typeof(this.env.contacts)!='object' || !this.env.contacts.length)
+ return true;
+
+ if (this.ksearch_timer)
+ clearTimeout(this.ksearch_timer);
+
+ if (!e)
+ e = window.event;
+
+ var highlight;
+ var key = e.keyCode ? e.keyCode : e.which;
+
+ switch (key)
+ {
+ case 38: // key up
+ case 40: // key down
+ if (!this.ksearch_pane)
+ break;
+
+ var dir = key==38 ? 1 : 0;
+ var next;
+
+ highlight = document.getElementById('rcmksearchSelected');
+ if (!highlight)
+ highlight = this.ksearch_pane.ul.firstChild;
+
+ if (highlight && (next = dir ? highlight.previousSibling : highlight.nextSibling))
+ {
+ highlight.removeAttribute('id');
+ //highlight.removeAttribute('class');
+ this.set_classname(highlight, 'selected', false);
+ }
+
+ if (next)
+ {
+ next.setAttribute('id', 'rcmksearchSelected');
+ this.set_classname(next, 'selected', true);
+ this.ksearch_selected = next._rcm_id;
+ }
+
+ if (e.preventDefault)
+ e.preventDefault();
+ return false;
+
+ case 9: // tab
+ if(e.shiftKey)
+ break;
+
+ case 13: // enter
+ if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
+ break;
+
+ // get cursor pos
+ var inp_value = this.ksearch_input.value.toLowerCase();
+ var cpos = this.get_caret_pos(this.ksearch_input);
+ var p = inp_value.lastIndexOf(this.ksearch_value, cpos);
+
+ // replace search string with full address
+ var pre = this.ksearch_input.value.substring(0, p);
+ var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length);
+ var insert = this.env.contacts[this.ksearch_selected]+', ';
+ this.ksearch_input.value = pre + insert + end;
+
+ //this.ksearch_input.value = this.ksearch_input.value.substring(0, p)+insert;
+
+ // set caret to insert pos
+ cpos = p+insert.length;
+ if (this.ksearch_input.setSelectionRange)
+ this.ksearch_input.setSelectionRange(cpos, cpos);
+
+ // hide ksearch pane
+ this.ksearch_hide();
+
+ if (e.preventDefault)
+ e.preventDefault();
+ return false;
+
+ case 27: // escape
+ this.ksearch_hide();
+ break;
+
+ }
+
+ // start timer
+ this.ksearch_timer = setTimeout(this.ref+'.ksearch_get_results()', 200);
+ this.ksearch_input = obj;
+
+ return true;
+ };
+
+
+ // address search processor
+ this.ksearch_get_results = function()
+ {
+ var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
+ if (inp_value===null)
+ return;
+
+ // get string from current cursor pos to last comma
+ var cpos = this.get_caret_pos(this.ksearch_input);
+ var p = inp_value.lastIndexOf(',', cpos-1);
+ var q = inp_value.substring(p+1, cpos);
+
+ // trim query string
+ q = q.replace(/(^\s+|\s+$)/g, '').toLowerCase();
+
+ if (!q.length || q==this.ksearch_value)
+ {
+ if (!q.length && this.ksearch_pane && this.ksearch_pane.visible)
+ this.ksearch_pane.show(0);
+
+ return;
+ }
+
+ this.ksearch_value = q;
+
+ // start searching the contact list
+ var a_results = new Array();
+ var a_result_ids = new Array();
+ var c=0;
+ for (var i=0; i<this.env.contacts.length; i++)
+ {
+ if (this.env.contacts[i].toLowerCase().indexOf(q)>=0)
+ {
+ a_results[c] = this.env.contacts[i];
+ a_result_ids[c++] = i;
+
+ if (c==15) // limit search results
+ break;
+ }
+ }
+
+ // display search results
+ if (c && a_results.length)
+ {
+ var p, ul, li;
+
+ // create results pane if not present
+ if (!this.ksearch_pane)
+ {
+ ul = document.createElement('UL');
+ this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000});
+ this.ksearch_pane.elm.appendChild(ul);
+ this.ksearch_pane.ul = ul;
+ }
+ else
+ ul = this.ksearch_pane.ul;
+
+ // remove all search results
+ ul.innerHTML = '';
+
+ // add each result line to list
+ for (i=0; i<a_results.length; i++)
+ {
+ li = document.createElement('LI');
+ li.innerHTML = a_results[i].replace(/</, '<').replace(/>/, '>');
+ li._rcm_id = a_result_ids[i];
+ ul.appendChild(li);
+ }
+
+ // check if last selected item is still in result list
+ if (this.ksearch_selected!==null)
+ {
+ p = find_in_array(this.ksearch_selected, a_result_ids);
+ if (p>=0 && ul.childNodes)
+ {
+ ul.childNodes[p].setAttribute('id', 'rcmksearchSelected');
+ this.set_classname(ul.childNodes[p], 'selected', true);
+ }
+ else
+ this.ksearch_selected = null;
+ }
+
+ // if no item selected, select the first one
+ if (this.ksearch_selected===null)
+ {
+ ul.firstChild.setAttribute('id', 'rcmksearchSelected');
+ this.set_classname(ul.firstChild, 'selected', true);
+ this.ksearch_selected = a_result_ids[0];
+ }
+
+ // resize the containing layer to fit the list
+ //this.ksearch_pane.resize(ul.offsetWidth, ul.offsetHeight);
+
+ // move the results pane right under the input box and make it visible
+ var pos = rcube_get_object_pos(this.ksearch_input);
+ this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight);
+ this.ksearch_pane.show(1);
+ }
+ // hide results pane
+ else
+ this.ksearch_hide();
+ };
+
+
+ this.ksearch_blur = function(e, obj)
+ {
+ if (this.ksearch_timer)
+ clearTimeout(this.ksearch_timer);
+
+ this.ksearch_value = '';
+ this.ksearch_input = null;
+
+ this.ksearch_hide();
+ };
+
+
+ this.ksearch_hide = function()
+ {
+ this.ksearch_selected = null;
+
+ if (this.ksearch_pane)
+ this.ksearch_pane.show(0);
+ };
+
+
+
+ /*********************************************************/
+ /********* address book methods *********/
+ /*********************************************************/
+
+
+ this.list_contacts = function(page)
+ {
+ var add_url = '';
+ var target = window;
+
+ if (page && this.current_page==page)
+ return false;
+
+ // load contacts remotely
+ if (this.gui_objects.contactslist)
+ {
+ this.list_contacts_remote(page);
+ return;
+ }
+
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+ {
+ target = window.frames[this.env.contentframe];
+ add_url = '&_framed=1';
+ }
+
+ this.set_busy(true, 'loading');
+ location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url;
+ };
+
+
+ // send remote request to load contacts list
+ this.list_contacts_remote = function(page)
+ {
+ // clear list
+ var table = this.gui_objects.contactslist;
+ var tbody = document.createElement('TBODY');
+ table.insertBefore(tbody, table.tBodies[0]);
+ table.tBodies[1].style.display = 'none';
+
+ this.contact_rows = new Array();
+ this.list_rows = this.contact_rows;
+
+ // send request to server
+ var url = page ? '&_page='+page : '';
+ this.set_busy(true, 'loading');
+ this.http_request('list', url, true);
+ };
+
+
+ // load contact record
+ this.load_contact = function(cid, action, framed)
+ {
+ var add_url = '';
+ var target = window;
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+ {
+ add_url = '&_framed=1';
+ target = window.frames[this.env.contentframe];
+ document.getElementById(this.env.contentframe).style.visibility = 'inherit';
+ }
+ else if (framed)
+ return false;
+
+ //if (this.env.framed && add_url=='')
+
+ // add_url = '&_framed=1';
+
+ if (action && (cid || action=='add'))
+ {
+ this.set_busy(true);
+ target.location.href = this.env.comm_path+'&_action='+action+'&_cid='+cid+add_url;
+ }
+ return true;
+ };
+
+
+ this.delete_contacts = function()
+ {
+ // exit if no mailbox specified or if selection is empty
+ if (!(this.selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
+ return;
+
+ var a_cids = new Array();
+
+ if (this.env.cid)
+ a_cids[a_cids.length] = this.env.cid;
+ else
+ {
+ var id;
+ for (var n=0; n<this.selection.length; n++)
+ {
+ id = this.selection[n];
+ a_cids[a_cids.length] = id;
+
+ // 'remove' row from list (just hide it)
+ if (this.contact_rows[id].obj)
+ this.contact_rows[id].obj.style.display = 'none';
+ }
+
+ // hide content frame if we delete the currently displayed contact
+ if (this.selection.length==1 && this.env.contentframe)
+ {
+ var elm = document.getElementById(this.env.contentframe);
+ elm.style.visibility = 'hidden';
+ }
+ }
+
+ // send request to server
+ this.http_request('delete', '_cid='+a_cids.join(',')+'&_from='+(this.env.action ? this.env.action : ''));
+ return true;
+ };
+
+
+ // update a contact record in the list
+ this.update_contact_row = function(cid, cols_arr)
+ {
+ if (!this.contact_rows[cid] || !this.contact_rows[cid].obj)
+ return false;
+
+ var row = this.contact_rows[cid].obj;
+ for (var c=0; c<cols_arr.length; c++){
+ if (row.cells[c])
+ row.cells[c].innerHTML = cols_arr[c];
+ }
+ return true;
+ };
+
+
+ // load ldap search form
+ this.ldappublicsearch = function(action)
+ {
+ var add_url = '';
+ var target = window;
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+ {
+ add_url = '&_framed=1';
+ target = window.frames[this.env.contentframe];
+ document.getElementById(this.env.contentframe).style.visibility = 'inherit';
+ }
+ else
+ return false;
+
+
+ if (action == 'ldappublicsearch')
+ target.location.href = this.env.comm_path+'&_action='+action+add_url;
+
+ return true;
+ };
+
+ // add ldap contacts to address book
+ this.add_ldap_contacts = function()
+ {
+ if (window.frames[this.env.contentframe].rcmail)
+ {
+ var frame = window.frames[this.env.contentframe];
+
+ // build the url
+ var url = '&_framed=1';
+ var emails = '&_emails=';
+ var names = '&_names=';
+ var end = '';
+ for (var n=0; n<frame.rcmail.selection.length; n++)
+ {
+ end = n < frame.rcmail.selection.length - 1 ? ',' : '';
+ emails += frame.rcmail.ldap_contact_rows[frame.rcmail.selection[n]].obj.cells[1].innerHTML + end;
+ names += frame.rcmail.ldap_contact_rows[frame.rcmail.selection[n]].obj.cells[0].innerHTML + end;
+ }
+
+ frame.location.href = this.env.comm_path + '&_action=save&_framed=1' + emails + names;
+ }
+ return false;
+ }
+
+
+
+ /*********************************************************/
+ /********* user settings methods *********/
+ /*********************************************************/
+
+
+ // load contact record
+ this.load_identity = function(id, action)
+ {
+ if (action=='edit-identity' && (!id || id==this.env.iid))
+ return false;
+
+ var add_url = '';
+ var target = window;
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+ {
+ add_url = '&_framed=1';
+ target = window.frames[this.env.contentframe];
+ document.getElementById(this.env.contentframe).style.visibility = 'inherit';
+ }
+
+ if (action && (id || action=='add-identity'))
+ {
+ this.set_busy(true);
+ target.location.href = this.env.comm_path+'&_action='+action+'&_iid='+id+add_url;
+ }
+ return true;
+ };
+
+
+
+ this.delete_identity = function(id)
+ {
+ // exit if no mailbox specified or if selection is empty
+ if (!(this.selection.length || this.env.iid))
+ return;
+
+ if (!id)
+ id = this.env.iid ? this.env.iid : this.selection[0];
+
+/*
+ // 'remove' row from list (just hide it)
+ if (this.identity_rows && this.identity_rows[id].obj)
+ {
+ this.clear_selection();
+ this.identity_rows[id].obj.style.display = 'none';
+ }
+*/
+
+ // if (this.env.framed && id)
+ this.set_busy(true);
+ location.href = this.env.comm_path+'&_action=delete-identity&_iid='+id;
+ // else if (id)
+ // this.http_request('delete-identity', '_iid='+id);
+ return true;
+ };
+
+
+ // tell server to create and subscribe a new mailbox
+ this.create_folder = function(name)
+ {
+ if (this.edit_folder)
+ this.reset_folder_rename();
+
+ var form;
+ if ((form = this.gui_objects.editform) && form.elements['_folder_name'])
+ name = form.elements['_folder_name'].value;
+
+ if (name)
+ this.http_request('create-folder', '_name='+escape(name), true);
+ else if (form.elements['_folder_name'])
+ form.elements['_folder_name'].focus();
+ };
+
+
+ // entry point for folder renaming
+ this.rename_folder = function(props)
+ {
+ var form, oldname, newname;
+
+ // rename a specific mailbox
+ if (props)
+ this.edit_foldername(props);
+
+ // use a dropdown and input field (old behavior)
+ else if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'] && form.elements['_folder_newname'])
+ {
+ oldname = form.elements['_folder_oldname'].value;
+ newname = form.elements['_folder_newname'].value;
+ }
+
+ if (oldname && newname)
+ this.http_request('rename-folder', '_folder_oldname='+escape(oldname)+'&_folder_newname='+escape(newname));
+ };
+
+
+ // start editing the mailbox name.
+ // this will replace the name string with an input field
+ this.edit_foldername = function(folder)
+ {
+ var temp, row, form;
+ var id = this.get_folder_row_id(folder);
+
+ // reset current renaming
+ if (temp = this.edit_folder)
+ {
+ this.reset_folder_rename();
+ if (temp == id)
+ return;
+ }
+
+ if (id && (row = document.getElementById(id)))
+ {
+ this.name_input = document.createElement('INPUT');
+ this.name_input.value = this.env.subscriptionrows[id];
+ this.name_input.style.width = '100%';
+ this.name_input.onkeypress = function(e){ rcmail.name_input_keypress(e); };
+
+ row.cells[0].replaceChild(this.name_input, row.cells[0].firstChild);
+ this.edit_folder = id;
+ this.name_input.select();
+
+ if (form = this.gui_objects.editform)
+ form.onsubmit = function(){ return false; };
+ }
+ };
+
+
+ // remove the input field and write the current mailbox name to the table cell
+ this.reset_folder_rename = function()
+ {
+ var cell = this.name_input ? this.name_input.parentNode : null;
+ if (cell && this.edit_folder)
+ cell.innerHTML = this.env.subscriptionrows[this.edit_folder];
+
+ this.edit_folder = null;
+ };
+
+
+ // handler for keyboard events on the input field
+ this.name_input_keypress = function(e)
+ {
+ var key = document.all ? event.keyCode : document.getElementById ? e.keyCode : 0;
+
+ // enter
+ if (key==13)
+ {
+ var newname = this.name_input ? this.name_input.value : null;
+ if (this.edit_folder && newname)
+ this.http_request('rename-folder', '_folder_oldname='+escape(this.env.subscriptionrows[this.edit_folder])+'&_folder_newname='+escape(newname));
+ }
+ // escape
+ else if (key==27)
+ this.reset_folder_rename();
+ };
+
+
+ // delete a specific mailbox with all its messages
+ this.delete_folder = function(folder)
+ {
+ if (this.edit_folder)
+ this.reset_folder_rename();
+
+ if (folder)
+ this.http_request('delete-folder', '_mboxes='+escape(folder));
+ };
+
+
+ // add a new folder to the subscription list by cloning a folder row
+ this.add_folder_row = function(name, replace)
+ {
+ name = name.replace('\\',"");
+ if (!this.gui_objects.subscriptionlist)
+ return false;
+
+ for (var refid in this.env.subscriptionrows)
+ if (this.env.subscriptionrows[refid]!=null)
+ break;
+
+ var refrow, form;
+ var tbody = this.gui_objects.subscriptionlist.tBodies[0];
+ var id = replace && replace.id ? replace.id : tbody.childNodes.length+1;
+
+ if (!id || !(refrow = document.getElementById(refid)))
+ {
+ // Refresh page if we don't have a table row to clone
+ location.href = this.env.comm_path+'&_action=folders';
+ }
+ else
+ {
+ // clone a table row if there are existing rows
+ var row = this.clone_table_row(refrow);
+ row.id = 'rcmrow'+id;
+ if (replace)
+ tbody.replaceChild(row, replace);
+ else
+ tbody.appendChild(row);
+ }
+
+ // add to folder/row-ID map
+ this.env.subscriptionrows[row.id] = name;
+
+ // set folder name
+ row.cells[0].innerHTML = name;
+ if (row.cells[1] && row.cells[1].firstChild.tagName=='INPUT')
+ {
+ row.cells[1].firstChild.value = name;
+ row.cells[1].firstChild.checked = true;
+ }
+
+ if (row.cells[2] && row.cells[2].firstChild.tagName=='A')
+ row.cells[2].firstChild.onclick = new Function(this.ref+".command('rename-folder','"+name.replace('\'','\\\'')+"')");
+ if (row.cells[3] && row.cells[3].firstChild.tagName=='A')
+ row.cells[3].firstChild.onclick = new Function(this.ref+".command('delete-folder','"+name.replace('\'','\\\'')+"')");
+
+ // add new folder to rename-folder list and clear input field
+ if (!replace && (form = this.gui_objects.editform))
+ {
+ if (form.elements['_folder_oldname'])
+ form.elements['_folder_oldname'].options[form.elements['_folder_oldname'].options.length] = new Option(name,name);
+ if (form.elements['_folder_name'])
+ form.elements['_folder_name'].value = '';
+ }
+
+ };
+
+
+ // replace an existing table row with a new folder line
+ this.replace_folder_row = function(newfolder, oldfolder)
+ {
+ var id = this.get_folder_row_id(oldfolder);
+ var row = document.getElementById(id);
+
+ // replace an existing table row (if found)
+ this.add_folder_row(newfolder, row);
+ this.env.subscriptionrows[id] = null;
+
+ // rename folder in rename-folder dropdown
+ var form, elm;
+ if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname']))
+ {
+ for (var i=0;i<elm.options.length;i++)
+ {
+ if (elm.options[i].value == oldfolder)
+ {
+ elm.options[i].text = newfolder;
+ elm.options[i].value = newfolder;
+ break;
+ }
+ }
+
+ form.elements['_folder_newname'].value = '';
+ }
+ };
+
+
+ // remove the table row of a specific mailbox from the table
+ // (the row will not be removed, just hidden)
+ this.remove_folder_row = function(folder)
+ {
+ var row;
+ var id = this.get_folder_row_id(folder);
+ if (id && (row = document.getElementById(id)))
+ row.style.display = 'none';
+
+ // remove folder from rename-folder list
+ var form;
+ if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'])
+ {
+ for (var i=0;i<form.elements['_folder_oldname'].options.length;i++)
+ {
+ if (form.elements['_folder_oldname'].options[i].value == folder)
+ {
+ form.elements['_folder_oldname'].options[i] = null;
+ break;
+ }
+ }
+ }
+
+ if (form && form.elements['_folder_newname'])
+ form.elements['_folder_newname'].value = '';
+ };
+
+
+ this.subscribe_folder = function(folder)
+ {
+ var form;
+ if ((form = this.gui_objects.editform) && form.elements['_unsubscribed'])
+ this.change_subscription('_unsubscribed', '_subscribed', 'subscribe');
+ else if (folder)
+ this.http_request('subscribe', '_mboxes='+escape(folder));
+ };
+
+
+ this.unsubscribe_folder = function(folder)
+ {
+ var form;
+ if ((form = this.gui_objects.editform) && form.elements['_subscribed'])
+ this.change_subscription('_subscribed', '_unsubscribed', 'unsubscribe');
+ else if (folder)
+ this.http_request('unsubscribe', '_mboxes='+escape(folder));
+ };
+
+
+ this.change_subscription = function(from, to, action)
+ {
+ var form;
+ if (form = this.gui_objects.editform)
+ {
+ var a_folders = new Array();
+ var list_from = form.elements[from];
+
+ for (var i=0; list_from && i<list_from.options.length; i++)
+ {
+ if (list_from.options[i] && list_from.options[i].selected)
+ {
+ a_folders[a_folders.length] = list_from.options[i].value;
+ list_from[i] = null;
+ i--;
+ }
+ }
+
+ // yes, we have some folders selected
+ if (a_folders.length)
+ {
+ var list_to = form.elements[to];
+ var index;
+
+ for (var n=0; n<a_folders.length; n++)
+ {
+ index = list_to.options.length;
+ list_to[index] = new Option(a_folders[n]);
+ }
+
+ this.http_request(action, '_mboxes='+escape(a_folders.join(',')));
+ }
+ }
+
+ };
+
+ // helper method to find a specific mailbox row ID
+ this.get_folder_row_id = function(folder)
+ {
+ for (var id in this.env.subscriptionrows)
+ if (this.env.subscriptionrows[id]==folder)
+ break;
+
+ return id;
+ };
+
+ // duplicate a specific table row
+ this.clone_table_row = function(row)
+ {
+ var cell, td;
+ var new_row = document.createElement('TR');
+ for(var n=0; n<row.childNodes.length; n++)
+ {
+ cell = row.childNodes[n];
+ td = document.createElement('TD');
+
+ if (cell.className)
+ td.className = cell.className;
+ if (cell.align)
+ td.setAttribute('align', cell.align);
+
+ td.innerHTML = cell.innerHTML;
+ new_row.appendChild(td);
+ }
+
+ return new_row;
+ };
+
+
+ /*********************************************************/
+ /********* GUI functionality *********/
+ /*********************************************************/
+
+
+ // eable/disable buttons for page shifting
+ this.set_page_buttons = function()
+ {
+ this.enable_command('nextpage', (this.env.pagecount > this.env.current_page));
+ this.enable_command('previouspage', (this.env.current_page > 1));
+ }
+
+
+ // set button to a specific state
+ this.set_button = function(command, state)
+ {
+ var a_buttons = this.buttons[command];
+ var button, obj;
+
+ if(!a_buttons || !a_buttons.length)
+ return;
+
+ for(var n=0; n<a_buttons.length; n++)
+ {
+ button = a_buttons[n];
+ obj = document.getElementById(button.id);
+
+ // get default/passive setting of the button
+ if (obj && button.type=='image' && !button.status)
+ button.pas = obj._original_src ? obj._original_src : obj.src;
+ else if (obj && !button.status)
+ button.pas = String(obj.className);
+
+ // set image according to button state
+ if (obj && button.type=='image' && button[state])
+ {
+ button.status = state;
+ obj.src = button[state];
+ }
+ // set class name according to button state
+ else if (obj && typeof(button[state])!='undefined')
+ {
+ button.status = state;
+ obj.className = button[state];
+ }
+ // disable/enable input buttons
+ if (obj && button.type=='input')
+ {
+ button.status = state;
+ obj.disabled = !state;
+ }
+ }
+ };
+
+
+ // mouse over button
+ this.button_over = function(command, id)
+ {
+ var a_buttons = this.buttons[command];
+ var button, img;
+
+ if(!a_buttons || !a_buttons.length)
+ return;
+
+ for(var n=0; n<a_buttons.length; n++)
+ {
+ button = a_buttons[n];
+ if(button.id==id && button.status=='act')
+ {
+ img = document.getElementById(button.id);
+ if (img && button.over)
+ img.src = button.over;
+ }
+ }
+ };
+
+ // mouse down on button
+ this.button_sel = function(command, id)
+ {
+ var a_buttons = this.buttons[command];
+ var button, img;
+
+ if(!a_buttons || !a_buttons.length)
+ return;
+
+ for(var n=0; n<a_buttons.length; n++)
+ {
+ button = a_buttons[n];
+ if(button.id==id && button.status=='act')
+ {
+ img = document.getElementById(button.id);
+ if (img && button.sel)
+ img.src = button.sel;
+ }
+ }
+ };
+
+ // mouse out of button
+ this.button_out = function(command, id)
+ {
+ var a_buttons = this.buttons[command];
+ var button, img;
+
+ if(!a_buttons || !a_buttons.length)
+ return;
+
+ for(var n=0; n<a_buttons.length; n++)
+ {
+ button = a_buttons[n];
+ if(button.id==id && button.status=='act')
+ {
+ img = document.getElementById(button.id);
+ if (img && button.act)
+ img.src = button.act;
+ }
+ }
+ };
+
+
+ // set/unset a specific class name
+ this.set_classname = function(obj, classname, set)
+ {
+ var reg = new RegExp('\s*'+classname, 'i');
+ if (!set && obj.className.match(reg))
+ obj.className = obj.className.replace(reg, '');
+ else if (set && !obj.className.match(reg))
+ obj.className += ' '+classname;
+ };
+
+
+ // display a specific alttext
+ this.alttext = function(text)
+ {
+
+ };
+
+
+ // display a system message
+ this.display_message = function(msg, type, hold)
+ {
+ this.set_busy(false);
+ if (!this.loaded) // save message in order to display after page loaded
+ {
+ this.pending_message = new Array(msg, type);
+ return true;
+ }
+
+ if (!this.gui_objects.message)
+ return false;
+
+ if (this.message_timer)
+ clearTimeout(this.message_timer);
+
+ var cont = msg;
+ if (type)
+ cont = '<div class="'+type+'">'+cont+'</div>';
+
+ this.gui_objects.message._rcube = this;
+ this.gui_objects.message.innerHTML = cont;
+ this.gui_objects.message.style.display = 'block';
+
+ if (type!='loading')
+ this.gui_objects.message.onmousedown = function(){ this._rcube.hide_message(); return true; };
+
+ if (!hold)
+ this.message_timer = setTimeout(this.ref+'.hide_message()', this.message_time);
+ };
+
+
+ // make a message row disapear
+ this.hide_message = function()
+ {
+ if (this.gui_objects.message)
+ {
+ this.gui_objects.message.style.display = 'none';
+ this.gui_objects.message.onmousedown = null;
+ }
+ };
+
+
+ // mark a mailbox as selected and set environment variable
+ this.select_mailbox = function(mbox)
+ {
+ if (this.gui_objects.mailboxlist )
+ {
+ var item, reg, text_obj;
+ var current_li = this.get_mailbox_li();
+ var mbox_li = this.get_mailbox_li(mbox);
+
+ if (current_li)
+ {
+ this.set_classname(current_li, 'selected', false);
+ this.set_classname(current_li, 'unfocused', false);
+ }
+
+ if (mbox_li || this.env.mailbox == mbox)
+ {
+ this.set_classname(mbox_li, 'unfocused', false);
+ this.set_classname(mbox_li, 'selected', true);
+ }
+ }
+
+ // also update mailbox name in window title
+ if (document.title)
+ {
+ var doc_title = String(document.title);
+ var reg = new RegExp(this.env.mailbox.toLowerCase(), 'i');
+ if (this.env.mailbox && doc_title.match(reg))
+ document.title = doc_title.replace(reg, mbox).replace(/^\([0-9]+\)\s+/i, '');
+ }
+
+ this.env.mailbox = mbox;
+ };
+
+
+ // for reordering column array, Konqueror workaround
+ this.set_message_coltypes = function(coltypes)
+ {
+ this.coltypes = coltypes;
+
+ // set correct list titles
+ var cell, col;
+ var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null;
+ for (var n=0; thead && n<this.coltypes.length; n++)
+ {
+ col = this.coltypes[n];
+ if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to'))
+ {
+ // if we have links for sorting, it's a bit more complicated...
+ if (cell.firstChild && cell.firstChild.tagName=='A')
+ {
+ cell.firstChild.innerHTML = this.get_label(this.coltypes[n]);
+ cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); };
+ cell.firstChild.__col = col;
+ }
+ else
+ cell.innerHTML = this.get_label(this.coltypes[n]);
+
+ cell.id = 'rcmHead'+col;
+ }
+ }
+
+ };
+
+ // create a table row in the message list
+ this.add_message_row = function(uid, cols, flags, attachment, attop)
+ {
+ if (!this.gui_objects.messagelist || !this.gui_objects.messagelist.tBodies[0])
+ return false;
+
+ var tbody = this.gui_objects.messagelist.tBodies[0];
+ var rowcount = tbody.rows.length;
+ var even = rowcount%2;
+
+ this.env.messages[uid] = {deleted:flags.deleted?1:0,
+ replied:flags.replied?1:0,
+ unread:flags.unread?1:0};
+
+ var row = document.createElement('TR');
+ row.id = 'rcmrow'+uid;
+ row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '')+(flags.deleted ? ' deleted' : '');
+
+ if (this.in_selection(uid))
+ row.className += ' selected';
+
+ var icon = flags.deleted && this.env.deletedicon ? this.env.deletedicon:
+ (flags.unread && this.env.unreadicon ? this.env.unreadicon :
+ (flags.replied && this.env.repliedicon ? this.env.repliedicon : this.env.messageicon));
+
+ var col = document.createElement('TD');
+ col.className = 'icon';
+ col.innerHTML = icon ? '<img src="'+icon+'" alt="" border="0" />' : '';
+ row.appendChild(col);
+
+ // add each submitted col
+ for (var n = 0; n < this.coltypes.length; n++)
+ {
+ var c = this.coltypes[n];
+ col = document.createElement('TD');
+ col.className = String(c).toLowerCase();
+ col.innerHTML = cols[c];
+ row.appendChild(col);
+ }
+
+ col = document.createElement('TD');
+ col.className = 'icon';
+ col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" border="0" />' : '';
+ row.appendChild(col);
+
+ if (attop && tbody.rows.length)
+ tbody.insertBefore(row, tbody.firstChild);
+ else
+ tbody.appendChild(row);
+
+ this.init_message_row(row);
+ };
+
+
+ // replace content of row count display
+ this.set_rowcount = function(text)
+ {
+ if (this.gui_objects.countdisplay)
+ this.gui_objects.countdisplay.innerHTML = text;
+
+ // update page navigation buttons
+ this.set_page_buttons();
+ };
+
+ // replace content of quota display
+ this.set_quota = function(text)
+ {
+ if (this.gui_objects.quotadisplay)
+ this.gui_objects.quotadisplay.innerHTML = text;
+ };
+
+
+ // update the mailboxlist
+ this.set_unread_count = function(mbox, count, set_title)
+ {
+ if (!this.gui_objects.mailboxlist)
+ return false;
+
+ if (mbox==this.env.mailbox)
+ set_title = true;
+
+ var reg, text_obj;
+ var item = this.get_mailbox_li(mbox);
+ mbox = String(mbox).toLowerCase().replace(this.mbox_expression, '');
+
+ if (item && item.className && item.className.indexOf('mailbox '+mbox)>=0)
+ {
+ // set new text
+ text_obj = item.firstChild;
+ reg = /\s+\([0-9]+\)$/i;
+
+ if (count && text_obj.innerHTML.match(reg))
+ text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+count+')');
+ else if (count)
+ text_obj.innerHTML += ' ('+count+')';
+ else
+ text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
+
+ // set the right classes
+ this.set_classname(item, 'unread', count>0 ? true : false);
+ }
+
+ // set unread count to window title
+ reg = /^\([0-9]+\)\s+/i;
+ if (set_title && document.title)
+ {
+ var doc_title = String(document.title);
+
+ if (count && doc_title.match(reg))
+ document.title = doc_title.replace(reg, '('+count+') ');
+ else if (count)
+ document.title = '('+count+') '+doc_title;
+ else
+ document.title = doc_title.replace(reg, '');
+ }
+ };
+
+
+ // add row to contacts list
+ this.add_contact_row = function(cid, cols)
+ {
+ if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
+ return false;
+
+ var tbody = this.gui_objects.contactslist.tBodies[0];
+ var rowcount = tbody.rows.length;
+ var even = rowcount%2;
+
+ var row = document.createElement('TR');
+ row.id = 'rcmrow'+cid;
+ row.className = 'contact '+(even ? 'even' : 'odd');
+
+ if (this.in_selection(cid))
+ row.className += ' selected';
+
+ // add each submitted col
+ for (var c in cols)
+ {
+ col = document.createElement('TD');
+ col.className = String(c).toLowerCase();
+ col.innerHTML = cols[c];
+ row.appendChild(col);
+ }
+
+ tbody.appendChild(row);
+ this.init_table_row(row, 'contact_rows');
+ };
+
+
+
+ /********************************************************/
+ /********* drag & drop methods *********/
+ /********************************************************/
+
+
+ this.drag_mouse_move = function(e)
+ {
+ if (this.drag_start)
+ {
+ if (!this.draglayer)
+ this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000});
+
+ // get subjects of selectedd messages
+ var names = '';
+ var c, subject, obj;
+ for(var n=0; n<this.selection.length; n++)
+ {
+ if (n>12) // only show 12 lines
+ {
+ names += '...';
+ break;
+ }
+
+ if (this.message_rows[this.selection[n]].obj)
+ {
+ obj = this.message_rows[this.selection[n]].obj;
+ subject = '';
+
+ for(c=0; c<obj.childNodes.length; c++)
+ if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3)
+ {
+ subject = obj.childNodes[c].firstChild.data;
+ names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />';
+ }
+ }
+ }
+
+ this.draglayer.write(names);
+ this.draglayer.show(1);
+ }
+
+ var pos = this.get_mouse_pos(e);
+ this.draglayer.move(pos.x+20, pos.y-5);
+
+ this.drag_start = false;
+ this.drag_active = true;
+
+ return false;
+ };
+
+
+ this.drag_mouse_up = function()
+ {
+ document.onmousemove = null;
+
+ if (this.draglayer && this.draglayer.visible)
+ this.draglayer.show(0);
+
+ this.drag_active = false;
+
+ return false;
+ };
+
+
+
+ /********************************************************/
+ /********* remote request methods *********/
+ /********************************************************/
+
+
+ this.http_sockets = new Array();
+
+ // find a non-busy socket or create a new one
+ this.get_request_obj = function()
+ {
+ for (var n=0; n<this.http_sockets.length; n++)
+ {
+ if (!this.http_sockets[n].busy)
+ return this.http_sockets[n];
+ }
+
+ // create a new XMLHTTP object
+ var i = this.http_sockets.length;
+ this.http_sockets[i] = new rcube_http_request();
+
+ return this.http_sockets[i];
+ };
+
+
+ // send a http request to the server
+ this.http_request = function(action, querystring, lock)
+ {
+ var request_obj = this.get_request_obj();
+ querystring += '&_remote=1';
+
+ // add timestamp to request url to avoid cacheing problems in Safari
+ if (bw.safari)
+ querystring += '&_ts='+(new Date().getTime());
+
+ // send request
+ if (request_obj)
+ {
+ // prompt('request', this.env.comm_path+'&_action='+escape(action)+'&'+querystring);
+ console('HTTP request: '+this.env.comm_path+'&_action='+escape(action)+'&'+querystring);
+
+ if (lock)
+ this.set_busy(true);
+
+ request_obj.__lock = lock ? true : false;
+ request_obj.__action = action;
+ request_obj.onerror = function(o){ rcube_webmail_client.http_error(o); };
+ request_obj.oncomplete = function(o){ rcube_webmail_client.http_response(o); };
+ request_obj.GET(this.env.comm_path+'&_action='+escape(action)+'&'+querystring);
+ }
+ };
+
+
+ // handle HTTP response
+ this.http_response = function(request_obj)
+ {
+ var ctype = request_obj.get_header('Content-Type');
+ if (ctype){
+ ctype = String(ctype).toLowerCase();
+ var ctype_array=ctype.split(";");
+ ctype = ctype_array[0];
+ }
+
+ this.set_busy(false);
+
+ console(request_obj.get_text());
+
+ // if we get javascript code from server -> execute it
+ if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript'))
+ eval(request_obj.get_text());
+
+ // process the response data according to the sent action
+ switch (request_obj.__action)
+ {
+ case 'delete':
+ case 'moveto':
+ if (this.env.action=='show')
+ this.command('list');
+ break;
+
+ case 'list':
+ if (this.env.messagecount)
+ this.enable_command('purge', (this.env.mailbox==this.env.trash_mailbox));
+
+ case 'expunge':
+ this.enable_command('select-all', 'select-none', 'expunge', this.env.messagecount ? true : false);
+ break;
+ }
+
+ request_obj.reset();
+ };
+
+
+ // handle HTTP request errors
+ this.http_error = function(request_obj)
+ {
+ //alert('Error sending request: '+request_obj.url);
+
+ if (request_obj.__lock)
+ this.set_busy(false);
+
+ request_obj.reset();
+ request_obj.__lock = false;
+ };
+
+
+ // use an image to send a keep-alive siganl to the server
+ this.send_keep_alive = function()
+ {
+ var d = new Date();
+ this.http_request('keep-alive', '_t='+d.getTime());
+ };
+
+
+ // send periodic request to check for recent messages
+ this.check_for_recent = function()
+ {
+ if (this.busy)
+ {
+ this.send_keep_alive();
+ return;
+ }
+
+ this.set_busy(true, 'checkingmail');
+ var d = new Date();
+ this.http_request('check-recent', '_t='+d.getTime());
+ };
+
+
+ /********************************************************/
+ /********* helper methods *********/
+ /********************************************************/
+
+ // check if we're in show mode or if we have a unique selection
+ // and return the message uid
+ this.get_single_uid = function()
+ {
+ return this.env.uid ? this.env.uid : (this.selection.length==1 ? this.selection[0] : null);
+ };
+
+ // same as above but for contacts
+ this.get_single_cid = function()
+ {
+ return this.env.cid ? this.env.cid : (this.selection.length==1 ? this.selection[0] : null);
+ };
+
+
+/* deprecated methods
+
+ // check if Shift-key is pressed on event
+ this.check_shiftkey = function(e)
+ {
+ if(!e && window.event)
+ e = window.event;
+
+ if(bw.linux && bw.ns4 && e.modifiers)
+ return true;
+ else if((bw.ns4 && e.modifiers & Event.SHIFT_MASK) || (e && e.shiftKey))
+ return true;
+ else
+ return false;
+ }
+
+ // check if Shift-key is pressed on event
+ this.check_ctrlkey = function(e)
+ {
+ if(!e && window.event)
+ e = window.event;
+
+ if(bw.linux && bw.ns4 && e.modifiers)
+ return true;
+ else if (bw.mac)
+ return this.check_shiftkey(e);
+ else if((bw.ns4 && e.modifiers & Event.CTRL_MASK) || (e && e.ctrlKey))
+ return true;
+ else
+ return false;
+ }
+*/
+
+ // returns modifier key (constants defined at top of file)
+ this.get_modifier = function(e)
+ {
+ var opcode = 0;
+ e = e || window.event;
+
+ if (bw.mac && e)
+ {
+ opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
+ return opcode;
+ }
+ if (e)
+ {
+ opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
+ return opcode;
+ }
+ if (e.cancelBubble)
+ {
+ e.cancelBubble = true;
+ e.returnValue = false;
+ }
+ else if (e.preventDefault)
+ e.preventDefault();
+ }
+
+
+ this.get_mouse_pos = function(e)
+ {
+ if(!e) e = window.event;
+ var mX = (e.pageX) ? e.pageX : e.clientX;
+ var mY = (e.pageY) ? e.pageY : e.clientY;
+
+ if(document.body && document.all)
+ {
+ mX += document.body.scrollLeft;
+ mY += document.body.scrollTop;
+ }
+
+ return { x:mX, y:mY };
+ };
+
+
+ this.get_caret_pos = function(obj)
+ {
+ if (typeof(obj.selectionEnd)!='undefined')
+ return obj.selectionEnd;
+
+ else if (document.selection && document.selection.createRange)
+ {
+ var range = document.selection.createRange();
+ if (range.parentElement()!=obj)
+ return 0;
+
+ var gm = range.duplicate();
+ if (obj.tagName=='TEXTAREA')
+ gm.moveToElementText(obj);
+ else
+ gm.expand('textedit');
+
+ gm.setEndPoint('EndToStart', range);
+ var p = gm.text.length;
+
+ return p<=obj.value.length ? p : -1;
+ }
+
+ else
+ return obj.value.length;
+ };
+
+
+ this.set_caret2start = function(obj)
+ {
+ if (obj.createTextRange)
+ {
+ var range = obj.createTextRange();
+ range.collapse(true);
+ range.select();
+ }
+ else if (obj.setSelectionRange)
+ obj.setSelectionRange(0,0);
+
+ obj.focus();
+ };
+
+
+ // set all fields of a form disabled
+ this.lock_form = function(form, lock)
+ {
+ if (!form || !form.elements)
+ return;
+
+ var type;
+ for (var n=0; n<form.elements.length; n++)
+ {
+ type = form.elements[n];
+ if (type=='hidden')
+ continue;
+
+ form.elements[n].disabled = lock;
+ }
+ };
+
+ } // end object rcube_webmail
+
+
+
+// class for HTTP requests
+function rcube_http_request()
+ {
+ this.url = '';
+ this.busy = false;
+ this.xmlhttp = null;
+
+
+ // reset object properties
+ this.reset = function()
+ {
+ // set unassigned event handlers
+ this.onloading = function(){ };
+ this.onloaded = function(){ };
+ this.oninteractive = function(){ };
+ this.oncomplete = function(){ };
+ this.onabort = function(){ };
+ this.onerror = function(){ };
+
+ this.url = '';
+ this.busy = false;
+ this.xmlhttp = null;
+ }
+
+
+ // create HTMLHTTP object
+ this.build = function()
+ {
+ if (window.XMLHttpRequest)
+ this.xmlhttp = new XMLHttpRequest();
+ else if (window.ActiveXObject)
+ this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+ else
+ {
+
+ }
+ }
+
+ // sedn GET request
+ this.GET = function(url)
+ {
+ this.build();
+
+ if (!this.xmlhttp)
+ {
+ this.onerror(this);
+ return false;
+ }
+
+ var ref = this;
+ this.url = url;
+ this.busy = true;
+
+ this.xmlhttp.onreadystatechange = function(){ ref.xmlhttp_onreadystatechange(); };
+ this.xmlhttp.open('GET', url);
+ this.xmlhttp.send(null);
+ };
+
+
+ this.POST = function(url, a_param)
+ {
+ // not implemented yet
+ };
+
+
+ // handle onreadystatechange event
+ this.xmlhttp_onreadystatechange = function()
+ {
+ if(this.xmlhttp.readyState == 1)
+ this.onloading(this);
+
+ else if(this.xmlhttp.readyState == 2)
+ this.onloaded(this);
+
+ else if(this.xmlhttp.readyState == 3)
+ this.oninteractive(this);
+
+ else if(this.xmlhttp.readyState == 4)
+ {
+ try {
+ if (this.xmlhttp.status == 0)
+ this.onabort(this);
+ else if(this.xmlhttp.status == 200)
+ this.oncomplete(this);
+ else
+ this.onerror(this);
+
+ this.busy = false;
+ }
+ catch(err)
+ {
+ this.onerror(this);
+ this.busy = false;
+ }
+ }
+ }
+
+ // getter method for HTTP headers
+ this.get_header = function(name)
+ {
+ return this.xmlhttp.getResponseHeader(name);
+ };
+
+ this.get_text = function()
+ {
+ return this.xmlhttp.responseText;
+ };
+
+ this.get_xml = function()
+ {
+ return this.xmlhttp.responseXML;
+ };
+
+ this.reset();
+
+ } // end class rcube_http_request
+
+
+
+function console(str)
+ {
+ if (document.debugform && document.debugform.console)
+ document.debugform.console.value += str+'\n--------------------------------------\n';
+ }
+
+
+// set onload handler
+window.onload = function(e)
+ {
+ if (window.rcube_webmail_client)
+ rcube_webmail_client.init();
+ };
--- /dev/null
+/*
+ +-----------------------------------------------------------------------+
+ | RoundCube common js library |
+ | |
+ | This file is part of the RoundCube web development suite |
+ | Copyright (C) 2005, RoundCube Dev, - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: common.js 289 2006-08-02 03:42:08Z cmcnulty $
+*/
+
+
+// default browsercheck
+function roundcube_browser()
+ {
+ this.ver = parseFloat(navigator.appVersion);
+ this.appver = navigator.appVersion;
+ this.agent = navigator.userAgent;
+ this.name = navigator.appName;
+ this.vendor = navigator.vendor ? navigator.vendor : '';
+ this.vendver = navigator.vendorSub ? parseFloat(navigator.vendorSub) : 0;
+ this.product = navigator.product ? navigator.product : '';
+ this.platform = String(navigator.platform).toLowerCase();
+ this.lang = (navigator.language) ? navigator.language.substring(0,2) :
+ (navigator.browserLanguage) ? navigator.browserLanguage.substring(0,2) :
+ (navigator.systemLanguage) ? navigator.systemLanguage.substring(0,2) : 'en';
+
+ this.win = (this.platform.indexOf('win')>=0) ? true : false;
+ this.mac = (this.platform.indexOf('mac')>=0) ? true : false;
+ this.linux = (this.platform.indexOf('linux')>=0) ? true : false;
+ this.unix = (this.platform.indexOf('unix')>=0) ? true : false;
+
+ this.dom = document.getElementById ? true : false;
+ this.dom2 = (document.addEventListener && document.removeEventListener);
+
+ this.ie = (document.all) ? true : false;
+ this.ie4 = (this.ie && !this.dom);
+ this.ie5 = (this.dom && this.appver.indexOf('MSIE 5')>0);
+ this.ie6 = (this.dom && this.appver.indexOf('MSIE 6')>0);
+
+ this.mz = (this.dom && this.ver>=5); // (this.dom && this.product=='Gecko')
+ this.ns = ((this.ver<5 && this.name=='Netscape') || (this.ver>=5 && this.vendor.indexOf('Netscape')>=0));
+ this.ns4 = (this.ns && parseInt(this.ver)==4);
+ this.ns6 = (this.ns && parseInt(this.vendver)==6); // (this.mz && this.ns) ? true : false;
+ this.ns7 = (this.ns && parseInt(this.vendver)==7); // this.agent.indexOf('Netscape/7')>0);
+ this.safari = (this.agent.toLowerCase().indexOf('safari')>0 || this.agent.toLowerCase().indexOf('applewebkit')>0);
+ this.konq = (this.agent.toLowerCase().indexOf('konqueror')>0);
+
+ this.opera = (window.opera) ? true : false;
+ this.opera5 = (this.opera5 && this.agent.indexOf('Opera 5')>0) ? true : false;
+ this.opera6 = (this.opera && this.agent.indexOf('Opera 6')>0) ? true : false;
+ this.opera7 = (this.opera && this.agent.indexOf('Opera 7')>0) ? true : false;
+
+ if(this.opera && window.RegExp)
+ this.vendver = (/opera(\s|\/)([0-9\.]+)/i.test(navigator.userAgent)) ? parseFloat(RegExp.$2) : -1;
+ else if(!this.vendver && this.safari)
+ this.vendver = (/(safari|applewebkit)\/([0-9]+)/i.test(this.agent)) ? parseInt(RegExp.$2) : 0;
+ else if((!this.vendver && this.mz) || this.agent.indexOf('Camino')>0)
+ this.vendver = (/rv:([0-9\.]+)/.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
+ else if(this.ie && window.RegExp)
+ this.vendver = (/msie\s+([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
+ else if(this.konq && window.RegExp)
+ this.vendver = (/khtml\/([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
+
+
+ // get real language out of safari's user agent
+ if(this.safari && (/;\s+([a-z]{2})-[a-z]{2}\)/i.test(this.agent)))
+ this.lang = RegExp.$1;
+
+ this.dhtml = ((this.ie4 && this.win) || this.ie5 || this.ie6 || this.ns4 || this.mz);
+ this.layers = this.ns4; // (document.layers);
+ this.div = (this.ie4 || this.dom);
+ this.vml = (this.win && this.ie && this.dom && !this.opera);
+ this.linkborder = (this.ie || this.mz);
+ this.rollover = (this.ver>=4 || (this.ns && this.ver>=3)); // (document.images) ? true : false;
+ this.pngalpha = (this.mz || (this.opera && this.vendver>=6) || (this.ie && this.mac && this.vendver>=5) ||
+ (this.ie && this.win && this.vendver>=5.5) || this.safari);
+ this.opacity = (this.mz || (this.ie && this.vendver>=5.5 && !this.opera) || (this.safari && this.vendver>=100));
+ this.cookies = navigator.cookieEnabled;
+
+ // test for XMLHTTP support
+ this.xmlhttp_test = function()
+ {
+ var activeX_test = new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");
+ this.xmlhttp = (window.XMLHttpRequest || (window.ActiveXObject && activeX_test())) ? true : false;
+ return this.xmlhttp;
+ }
+ }
+
+
+
+
+var rcube_layer_objects = new Array();
+
+function rcube_layer(id, attributes)
+ {
+ this.name = id;
+
+ // create a new layer in the current document
+ this.create = function(arg)
+ {
+ var l = (arg.x) ? arg.x : 0;
+ var t = (arg.y) ? arg.y : 0;
+ var w = arg.width;
+ var h = arg.height;
+ var z = arg.zindex;
+ var vis = arg.vis;
+ var parent = arg.parent;
+ var obj;
+
+ obj = document.createElement('DIV');
+ with(obj)
+ {
+ id = this.name;
+ with(style)
+ {
+ position = 'absolute';
+ visibility = (vis) ? (vis==2) ? 'inherit' : 'visible' : 'hidden';
+ left = l+'px';
+ top = t+'px';
+ if(w) width = w+'px';
+ if(h) height = h+'px';
+ if(z) zIndex = z;
+ }
+ }
+
+ if(parent) parent.appendChild(obj);
+ else document.body.appendChild(obj);
+
+ this.elm = obj;
+ };
+
+
+ // create new layer
+ if(attributes!=null)
+ {
+ this.create(attributes);
+ this.name = this.elm.id;
+ }
+ else // just refer to the object
+ this.elm = document.getElementById(id);
+
+
+ if(!this.elm)
+ return false;
+
+
+ // ********* layer object properties *********
+
+ this.css = this.elm.style;
+ this.event = this.elm;
+ this.width = this.elm.offsetWidth;
+ this.height = this.elm.offsetHeight;
+ this.x = parseInt(this.elm.offsetLeft);
+ this.y = parseInt(this.elm.offsetTop);
+ this.visible = (this.css.visibility=='visible' || this.css.visibility=='show' || this.css.visibility=='inherit') ? true : false;
+
+ this.id = rcube_layer_objects.length;
+ this.obj = 'rcube_layer_objects['+this.id+']';
+ rcube_layer_objects[this.id] = this;
+
+
+ // ********* layer object methods *********
+
+
+ // move the layer to a specific position
+ this.move = function(x, y)
+ {
+ this.x = x;
+ this.y = y;
+ this.css.left = Math.round(this.x)+'px';
+ this.css.top = Math.round(this.y)+'px';
+ }
+
+
+ // move the layer for a specific step
+ this.shift = function(x,y)
+ {
+ x = Math.round(x*100)/100;
+ y = Math.round(y*100)/100;
+ this.move(this.x+x, this.y+y);
+ }
+
+
+ // change the layers width and height
+ this.resize = function(w,h)
+ {
+ this.css.width = w+'px';
+ this.css.height = h+'px';
+ this.width = w;
+ this.height = h;
+ }
+
+
+ // cut the layer (top,width,height,left)
+ this.clip = function(t,w,h,l)
+ {
+ this.css.clip='rect('+t+' '+w+' '+h+' '+l+')';
+ this.clip_height = h;
+ this.clip_width = w;
+ }
+
+
+ // show or hide the layer
+ this.show = function(a)
+ {
+ if(a==1)
+ {
+ this.css.visibility = 'visible';
+ this.visible = true;
+ }
+ else if(a==2)
+ {
+ this.css.visibility = 'inherit';
+ this.visible = true;
+ }
+ else
+ {
+ this.css.visibility = 'hidden';
+ this.visible = false;
+ }
+ }
+
+
+ // write new content into a Layer
+ this.write = function(cont)
+ {
+ this.elm.innerHTML = cont;
+ }
+
+
+ // set the given color to the layer background
+ this.set_bgcolor = function(c)
+ {
+ if(!c || c=='#')
+ c = 'transparent';
+
+ this.css.backgroundColor = c;
+ }
+
+
+ // set the opacity of a layer to the given ammount (in %)
+ this.set_opacity = function(v)
+ {
+ if(!bw.opacity)
+ return;
+
+ var op = v<=1 ? Math.round(v*100) : parseInt(v);
+
+ if(bw.ie)
+ this.css.filter = 'alpha(opacity:'+op+')';
+ else if(bw.safari)
+ {
+ this.css.opacity = op/100;
+ this.css.KhtmlOpacity = op/100;
+ }
+ else if(bw.mz)
+ this.css.MozOpacity = op/100;
+ }
+ }
+
+// check if input is a valid email address
+// By Cal Henderson <cal@iamcal.com>
+// http://code.iamcal.com/php/rfc822/
+function rcube_check_email(input, inline)
+ {
+ if (input && window.RegExp)
+ {
+ var no_ws_ctl = "[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]";
+ var alpha = "[\\x41-\\x5a\\x61-\\x7a]";
+ var digit = "[\\x30-\\x39]";
+ var cr = "\\x0d";
+ var lf = "\\x0a";
+ var crlf = "(" + cr + lf + ")";
+
+ var obs_char = "[\\x00-\\x09\\x0b\\x0c\\x0e-\\x7f]";
+ var obs_text = "("+lf+"*"+cr+"*("+obs_char+lf+"*"+cr+"*)*)";
+ var text = "([\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f]|"+obs_text+")";
+ var obs_qp = "(\\x5c[\\x00-\\x7f])";
+ var quoted_pair = "(\\x5c"+text+"|"+obs_qp+")";
+
+ var wsp = "[\\x20\\x09]";
+ var obs_fws = "("+wsp+"+("+crlf+wsp+"+)*)";
+ var fws = "((("+wsp+"*"+crlf+")?"+wsp+"+)|"+obs_fws+")";
+ var ctext = "("+no_ws_ctl+"|[\\x21-\\x27\\x2A-\\x5b\\x5d-\\x7e])";
+ var ccontent = "("+ctext+"|"+quoted_pair+")";
+ var comment = "(\\x28("+fws+"?"+ccontent+")*"+fws+"?\\x29)";
+ var cfws = "(("+fws+"?"+comment+")*("+fws+"?"+comment+"|"+fws+"))";
+ var cfws = fws+"*";
+
+ var atext = "("+alpha+"|"+digit+"|[\\x21\\x23-\\x27\\x2a\\x2b\\x2d\\x2e\\x3d\\x3f\\x5e\\x5f\\x60\\x7b-\\x7e])";
+ var atom = "("+cfws+"?"+atext+"+"+cfws+"?)";
+
+ var qtext = "("+no_ws_ctl+"|[\\x21\\x23-\\x5b\\x5d-\\x7e])";
+ var qcontent = "("+qtext+"|"+quoted_pair+")";
+ var quoted_string = "("+cfws+"?\\x22("+fws+"?"+qcontent+")*"+fws+"?\\x22"+cfws+"?)";
+ var word = "("+atom+"|"+quoted_string+")";
+
+ var obs_local_part = "("+word+"(\\x2e"+word+")*)";
+ var obs_domain = "("+atom+"(\\x2e"+atom+")*)";
+
+ var dot_atom_text = "("+atext+"+(\\x2e"+atext+"+)*)";
+ var dot_atom = "("+cfws+"?"+dot_atom_text+cfws+"?)";
+
+ var dtext = "("+no_ws_ctl+"|[\\x21-\\x5a\\x5e-\\x7e])";
+ var dcontent = "("+dtext+"|"+quoted_pair+")";
+ var domain_literal = "("+cfws+"?\\x5b("+fws+"?"+dcontent+")*"+fws+"?\\x5d"+cfws+"?)";
+
+ var local_part = "("+dot_atom+"|"+quoted_string+"|"+obs_local_part+")";
+ var domain = "("+dot_atom+"|"+domain_literal+"|"+obs_domain+")";
+ var addr_spec = "("+local_part+"\\x40"+domain+")";
+
+ var reg1 = inline ? new RegExp(addr_spec, 'i') : new RegExp('^'+addr_spec+'$', 'i');
+ return reg1.test(input) ? true : false;
+ }
+ return false;
+ }
+
+
+// find a value in a specific array and returns the index
+function find_in_array()
+ {
+ var args = find_in_array.arguments;
+ if(!args.length) return -1;
+
+ var haystack = typeof(args[0])=='object' ? args[0] : args.length>1 && typeof(args[1])=='object' ? args[1] : new Array();
+ var needle = typeof(args[0])!='object' ? args[0] : args.length>1 && typeof(args[1])!='object' ? args[1] : '';
+ var nocase = args.length==3 ? args[2] : false;
+
+ if(!haystack.length) return -1;
+
+ for(var i=0; i<haystack.length; i++)
+ if(nocase && haystack[i].toLowerCase()==needle.toLowerCase())
+ return i;
+ else if(haystack[i]==needle)
+ return i;
+
+ return -1;
+ }
+
+
+// get any type of html objects by id/name
+function rcube_find_object(id, d)
+ {
+ var n, f, obj, e;
+ if(!d) d = document;
+
+ if(d.getElementsByName && (e = d.getElementsByName(id)))
+ obj = e[0];
+ if(!obj && d.getElementById)
+ obj = d.getElementById(id);
+ if(!obj && d.all)
+ obj = d.all[id];
+
+ if(!obj && d.images.length)
+ obj = d.images[id];
+
+ if(!obj && d.forms.length)
+ for(f=0; f<d.forms.length; f++)
+ {
+ if(d.forms[f].name == id)
+ obj = d.forms[f];
+ else if(d.forms[f].elements[id])
+ obj = d.forms[f].elements[id];
+ }
+
+ if(!obj && d.layers)
+ {
+ if(d.layers[id]) obj = d.layers[id];
+ for(n=0; !obj && n<d.layers.length; n++)
+ obj = nex_get_object(id, d.layers[n].document);
+ }
+
+ return obj;
+ }
+
+
+// return the absolute position of an object within the document
+function rcube_get_object_pos(obj)
+ {
+ if(typeof(obj)=='string')
+ obj = nex_get_object(obj);
+
+ if(!obj) return {x:0, y:0};
+
+ var iX = (bw.layers) ? obj.x : obj.offsetLeft;
+ var iY = (bw.layers) ? obj.y : obj.offsetTop;
+
+ if(bw.ie || bw.mz)
+ {
+ var elm = obj.offsetParent;
+ while(elm && elm!=null)
+ {
+ iX += elm.offsetLeft;
+ iY += elm.offsetTop;
+ elm = elm.offsetParent;
+ }
+ }
+
+ if(bw.mac && bw.ie5) iX += document.body.leftMargin;
+ if(bw.mac && bw.ie5) iY += document.body.topMargin;
+
+ return {x:iX, y:iY};
+ }
+
+
+// cookie functions by GoogieSpell
+function setCookie(name, value, expires, path, domain, secure)
+ {
+ var curCookie = name + "=" + escape(value) +
+ (expires ? "; expires=" + expires.toGMTString() : "") +
+ (path ? "; path=" + path : "") +
+ (domain ? "; domain=" + domain : "") +
+ (secure ? "; secure" : "");
+ document.cookie = curCookie;
+ }
+
+function getCookie(name)
+ {
+ var dc = document.cookie;
+ var prefix = name + "=";
+ var begin = dc.indexOf("; " + prefix);
+ if (begin == -1)
+ {
+ begin = dc.indexOf(prefix);
+ if (begin != 0) return null;
+ }
+ else
+ begin += 2;
+ var end = document.cookie.indexOf(";", begin);
+ if (end == -1)
+ end = dc.length;
+ return unescape(dc.substring(begin + prefix.length, end));
+ }
+
+
+var bw = new roundcube_browser();
--- /dev/null
+/*
+Last Modified: 28/04/06 16:28:09
+
+ AmiJs library
+ A very small library with DOM and Ajax functions.
+ For a much larger script look on http://www.mochikit.com/
+ AUTHOR
+ 4mir Salihefendic (http://amix.dk) - amix@amix.dk
+ LICENSE
+ Copyright (c) 2006 Amir Salihefendic. All rights reserved.
+ Copyright (c) 2005 Bob Ippolito. All rights reserved.
+ http://www.opensource.org/licenses/mit-license.php
+ VERSION
+ 2.1
+ SITE
+ http://amix.dk/amijs
+**/
+
+var AJS = {
+////
+// Accessor functions
+////
+ /**
+ * @returns The element with the id
+ */
+ getElement: function(id) {
+ if(typeof(id) == "string")
+ return document.getElementById(id);
+ else
+ return id;
+ },
+
+ /**
+ * @returns The elements with the ids
+ */
+ getElements: function(/*id1, id2, id3*/) {
+ var elements = new Array();
+ for (var i = 0; i < arguments.length; i++) {
+ var element = this.getElement(arguments[i]);
+ elements.push(element);
+ }
+ return elements;
+ },
+
+ /**
+ * @returns The GET query argument
+ */
+ getQueryArgument: function(var_name) {
+ var query = window.location.search.substring(1);
+ var vars = query.split("&");
+ for (var i=0;i<vars.length;i++) {
+ var pair = vars[i].split("=");
+ if (pair[0] == var_name) {
+ return pair[1];
+ }
+ }
+ return null;
+ },
+
+ /**
+ * @returns If the browser is Internet Explorer
+ */
+ isIe: function() {
+ return (navigator.userAgent.toLowerCase().indexOf("msie") != -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1);
+ },
+
+ /**
+ * @returns The document body
+ */
+ getBody: function() {
+ return this.getElementsByTagAndClassName('body')[0]
+ },
+
+ /**
+ * @returns All the elements that have a specific tag name or class name
+ */
+ getElementsByTagAndClassName: function(tag_name, class_name, /*optional*/ parent) {
+ var class_elements = new Array();
+ if(!this.isDefined(parent))
+ parent = document;
+ if(!this.isDefined(tag_name))
+ tag_name = '*';
+
+ var els = parent.getElementsByTagName(tag_name);
+ var els_len = els.length;
+ var pattern = new RegExp("(^|\\s)" + class_name + "(\\s|$)");
+
+ for (i = 0, j = 0; i < els_len; i++) {
+ if ( pattern.test(els[i].className) || class_name == null ) {
+ class_elements[j] = els[i];
+ j++;
+ }
+ }
+ return class_elements;
+ },
+
+
+////
+// DOM manipulation
+////
+ /**
+ * Appends some nodes to a node
+ */
+ appendChildNodes: function(node/*, nodes...*/) {
+ if(arguments.length >= 2) {
+ for(var i=1; i < arguments.length; i++) {
+ var n = arguments[i];
+ if(typeof(n) == "string")
+ n = document.createTextNode(n);
+ if(this.isDefined(n))
+ node.appendChild(n);
+ }
+ }
+ return node;
+ },
+
+ /**
+ * Replaces a nodes children with another node(s)
+ */
+ replaceChildNodes: function(node/*, nodes...*/) {
+ var child;
+ while ((child = node.firstChild)) {
+ node.removeChild(child);
+ }
+ if (arguments.length < 2) {
+ return node;
+ } else {
+ return this.appendChildNodes.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Insert a node after another node
+ */
+ insertAfter: function(node, referenceNode) {
+ referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling);
+ },
+
+ /**
+ * Insert a node before another node
+ */
+ insertBefore: function(node, referenceNode) {
+ referenceNode.parentNode.insertBefore(node, referenceNode);
+ },
+
+ /**
+ * Shows the element
+ */
+ showElement: function(elm) {
+ elm.style.display = '';
+ },
+
+ /**
+ * Hides the element
+ */
+ hideElement: function(elm) {
+ elm.style.display = 'none';
+ },
+
+ isElementHidden: function(elm) {
+ return elm.style.visibility == "hidden";
+ },
+
+ /**
+ * Swaps one element with another. To delete use swapDOM(elm, null)
+ */
+ swapDOM: function(dest, src) {
+ dest = this.getElement(dest);
+ var parent = dest.parentNode;
+ if (src) {
+ src = this.getElement(src);
+ parent.replaceChild(src, dest);
+ } else {
+ parent.removeChild(dest);
+ }
+ return src;
+ },
+
+ /**
+ * Removes an element from the world
+ */
+ removeElement: function(elm) {
+ this.swapDOM(elm, null);
+ },
+
+ /**
+ * @returns Is an object a dictionary?
+ */
+ isDict: function(o) {
+ var str_repr = String(o);
+ return str_repr.indexOf(" Object") != -1;
+ },
+
+ /**
+ * Creates a DOM element
+ * @param {String} name The elements DOM name
+ * @param {Dict} attrs Attributes sent to the function
+ */
+ createDOM: function(name, attrs) {
+ var i=0;
+ elm = document.createElement(name);
+
+ if(this.isDict(attrs[i])) {
+ for(k in attrs[0]) {
+ if(k == "style")
+ elm.style.cssText = attrs[0][k];
+ else if(k == "class")
+ elm.className = attrs[0][k];
+ else
+ elm.setAttribute(k, attrs[0][k]);
+ }
+ i++;
+ }
+
+ if(attrs[0] == null)
+ i = 1;
+
+ for(i; i < attrs.length; i++) {
+ var n = attrs[i];
+ if(this.isDefined(n)) {
+ if(typeof(n) == "string")
+ n = document.createTextNode(n);
+ elm.appendChild(n);
+ }
+ }
+ return elm;
+ },
+
+ UL: function() { return this.createDOM.apply(this, ["ul", arguments]); },
+ LI: function() { return this.createDOM.apply(this, ["li", arguments]); },
+ TD: function() { return this.createDOM.apply(this, ["td", arguments]); },
+ TR: function() { return this.createDOM.apply(this, ["tr", arguments]); },
+ TH: function() { return this.createDOM.apply(this, ["th", arguments]); },
+ TBODY: function() { return this.createDOM.apply(this, ["tbody", arguments]); },
+ TABLE: function() { return this.createDOM.apply(this, ["table", arguments]); },
+ INPUT: function() { return this.createDOM.apply(this, ["input", arguments]); },
+ SPAN: function() { return this.createDOM.apply(this, ["span", arguments]); },
+ B: function() { return this.createDOM.apply(this, ["b", arguments]); },
+ A: function() { return this.createDOM.apply(this, ["a", arguments]); },
+ DIV: function() { return this.createDOM.apply(this, ["div", arguments]); },
+ IMG: function() { return this.createDOM.apply(this, ["img", arguments]); },
+ BUTTON: function() { return this.createDOM.apply(this, ["button", arguments]); },
+ H1: function() { return this.createDOM.apply(this, ["h1", arguments]); },
+ H2: function() { return this.createDOM.apply(this, ["h2", arguments]); },
+ H3: function() { return this.createDOM.apply(this, ["h3", arguments]); },
+ BR: function() { return this.createDOM.apply(this, ["br", arguments]); },
+ TEXTAREA: function() { return this.createDOM.apply(this, ["textarea", arguments]); },
+ FORM: function() { return this.createDOM.apply(this, ["form", arguments]); },
+ P: function() { return this.createDOM.apply(this, ["p", arguments]); },
+ SELECT: function() { return this.createDOM.apply(this, ["select", arguments]); },
+ OPTION: function() { return this.createDOM.apply(this, ["option", arguments]); },
+ TN: function(text) { return document.createTextNode(text); },
+ IFRAME: function() { return this.createDOM.apply(this, ["iframe", arguments]); },
+ SCRIPT: function() { return this.createDOM.apply(this, ["script", arguments]); },
+
+////
+// Ajax functions
+////
+ /**
+ * @returns A new XMLHttpRequest object
+ */
+ getXMLHttpRequest: function() {
+ var try_these = [
+ function () { return new XMLHttpRequest(); },
+ function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
+ function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
+ function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
+ function () { throw "Browser does not support XMLHttpRequest"; }
+ ];
+ for (var i = 0; i < try_these.length; i++) {
+ var func = try_these[i];
+ try {
+ return func();
+ } catch (e) {
+ }
+ }
+ },
+
+ /**
+ * Use this function to do a simple HTTP Request
+ */
+ doSimpleXMLHttpRequest: function(url) {
+ var req = this.getXMLHttpRequest();
+ req.open("GET", url, true);
+ return this.sendXMLHttpRequest(req);
+ },
+
+ getRequest: function(url, data) {
+ var req = this.getXMLHttpRequest();
+ req.open("POST", url, true);
+ req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ return this.sendXMLHttpRequest(req);
+ },
+
+ /**
+ * Send a XMLHttpRequest
+ */
+ sendXMLHttpRequest: function(req, data) {
+ var d = new AJSDeferred(req);
+
+ var onreadystatechange = function () {
+ if (req.readyState == 4) {
+ try {
+ status = req.status;
+ }
+ catch(e) {};
+ if(status == 200 || status == 304 || req.responseText == null) {
+ d.callback(req, data);
+ }
+ else {
+ d.errback();
+ }
+ }
+ }
+ req.onreadystatechange = onreadystatechange;
+ return d;
+ },
+
+ /**
+ * Represent an object as a string
+ */
+ reprString: function(o) {
+ return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
+ ).replace(/[\f]/g, "\\f"
+ ).replace(/[\b]/g, "\\b"
+ ).replace(/[\n]/g, "\\n"
+ ).replace(/[\t]/g, "\\t"
+ ).replace(/[\r]/g, "\\r");
+ },
+
+ /**
+ * Serialize an object to JSON notation
+ */
+ serializeJSON: function(o) {
+ var objtype = typeof(o);
+ if (objtype == "undefined") {
+ return "undefined";
+ } else if (objtype == "number" || objtype == "boolean") {
+ return o + "";
+ } else if (o === null) {
+ return "null";
+ }
+ if (objtype == "string") {
+ return this.reprString(o);
+ }
+ var me = arguments.callee;
+ var newObj;
+ if (typeof(o.__json__) == "function") {
+ newObj = o.__json__();
+ if (o !== newObj) {
+ return me(newObj);
+ }
+ }
+ if (typeof(o.json) == "function") {
+ newObj = o.json();
+ if (o !== newObj) {
+ return me(newObj);
+ }
+ }
+ if (objtype != "function" && typeof(o.length) == "number") {
+ var res = [];
+ for (var i = 0; i < o.length; i++) {
+ var val = me(o[i]);
+ if (typeof(val) != "string") {
+ val = "undefined";
+ }
+ res.push(val);
+ }
+ return "[" + res.join(",") + "]";
+ }
+ res = [];
+ for (var k in o) {
+ var useKey;
+ if (typeof(k) == "number") {
+ useKey = '"' + k + '"';
+ } else if (typeof(k) == "string") {
+ useKey = this.reprString(k);
+ } else {
+ // skip non-string or number keys
+ continue;
+ }
+ val = me(o[k]);
+ if (typeof(val) != "string") {
+ // skip non-serializable values
+ continue;
+ }
+ res.push(useKey + ":" + val);
+ }
+ return "{" + res.join(",") + "}";
+ },
+
+ /**
+ * Send and recive JSON using GET
+ */
+ loadJSONDoc: function(url) {
+ var d = this.getRequest(url);
+ var eval_req = function(req) {
+ var text = req.responseText;
+ return eval('(' + text + ')');
+ };
+ d.addCallback(eval_req);
+ return d;
+ },
+
+
+////
+// Misc.
+////
+ /**
+ * Alert the objects key attrs
+ */
+ keys: function(obj) {
+ var rval = [];
+ for (var prop in obj) {
+ rval.push(prop);
+ }
+ return rval;
+ },
+
+ urlencode: function(str) {
+ return encodeURIComponent(str.toString());
+ },
+
+ /**
+ * @returns True if the object is defined, otherwise false
+ */
+ isDefined: function(o) {
+ return (o != "undefined" && o != null)
+ },
+
+ /**
+ * @returns True if an object is a array, false otherwise
+ */
+ isArray: function(obj) {
+ try { return (typeof(obj.length) == "undefined") ? false : true; }
+ catch(e)
+ { return false; }
+ },
+
+ isObject: function(obj) {
+ return (obj && typeof obj == 'object');
+ },
+
+ /**
+ * Export DOM elements to the global namespace
+ */
+ exportDOMElements: function() {
+ UL = this.UL;
+ LI = this.LI;
+ TD = this.TD;
+ TR = this.TR;
+ TH = this.TH;
+ TBODY = this.TBODY;
+ TABLE = this.TABLE;
+ INPUT = this.INPUT;
+ SPAN = this.SPAN;
+ B = this.B;
+ A = this.A;
+ DIV = this.DIV;
+ IMG = this.IMG;
+ BUTTON = this.BUTTON;
+ H1 = this.H1;
+ H2 = this.H2;
+ H3 = this.H3;
+ BR = this.BR;
+ TEXTAREA = this.TEXTAREA;
+ FORM = this.FORM;
+ P = this.P;
+ SELECT = this.SELECT;
+ OPTION = this.OPTION;
+ TN = this.TN;
+ IFRAME = this.IFRAME;
+ SCRIPT = this.SCRIPT;
+ },
+
+ /**
+ * Export AmiJS functions to the global namespace
+ */
+ exportToGlobalScope: function() {
+ getElement = this.getElement;
+ getQueryArgument = this.getQueryArgument;
+ isIe = this.isIe;
+ $ = this.getElement;
+ getElements = this.getElements;
+ getBody = this.getBody;
+ getElementsByTagAndClassName = this.getElementsByTagAndClassName;
+ appendChildNodes = this.appendChildNodes;
+ ACN = appendChildNodes;
+ replaceChildNodes = this.replaceChildNodes;
+ RCN = replaceChildNodes;
+ insertAfter = this.insertAfter;
+ insertBefore = this.insertBefore;
+ showElement = this.showElement;
+ hideElement = this.hideElement;
+ isElementHidden = this.isElementHidden;
+ swapDOM = this.swapDOM;
+ removeElement = this.removeElement;
+ isDict = this.isDict;
+ createDOM = this.createDOM;
+ this.exportDOMElements();
+ getXMLHttpRequest = this.getXMLHttpRequest;
+ doSimpleXMLHttpRequest = this.doSimpleXMLHttpRequest;
+ getRequest = this.getRequest;
+ sendXMLHttpRequest = this.sendXMLHttpRequest;
+ reprString = this.reprString;
+ serializeJSON = this.serializeJSON;
+ loadJSONDoc = this.loadJSONDoc;
+ keys = this.keys;
+ isDefined = this.isDefined;
+ isArray = this.isArray;
+ }
+}
+
+
+
+AJSDeferred = function(req) {
+ this.callbacks = [];
+ this.req = req;
+
+ this.callback = function (res) {
+ while (this.callbacks.length > 0) {
+ var fn = this.callbacks.pop();
+ res = fn(res);
+ }
+ };
+
+ this.errback = function(e){
+ alert("Error encountered:\n" + e);
+ };
+
+ this.addErrback = function(fn) {
+ this.errback = fn;
+ };
+
+ this.addCallback = function(fn) {
+ this.callbacks.unshift(fn);
+ };
+
+ this.addCallbacks = function(fn1, fn2) {
+ this.addCallback(fn1);
+ this.addErrback(fn2);
+ };
+
+ this.sendReq = function(data) {
+ if(AJS.isObject(data)) {
+ var post_data = [];
+ for(k in data) {
+ post_data.push(k + "=" + AJS.urlencode(data[k]));
+ }
+ post_data = post_data.join("&");
+ this.req.send(post_data);
+ }
+ else if(AJS.isDefined(data))
+ this.req.send(data);
+ else {
+ this.req.send("");
+ }
+ };
+};
+AJSDeferred.prototype = new AJSDeferred();
+
+
+
+
+
+
+/****
+Last Modified: 28/04/06 15:26:06
+
+ GoogieSpell
+ Google spell checker for your own web-apps :)
+ Copyright Amir Salihefendic 2006
+ LICENSE
+ GPL (see gpl.txt for more information)
+ This basically means that you can't use this script with/in proprietary software!
+ There is another license that permits you to use this script with proprietary software. Check out:... for more info.
+ AUTHOR
+ 4mir Salihefendic (http://amix.dk) - amix@amix.dk
+ VERSION
+ 3.22
+****/
+var GOOGIE_CUR_LANG = "en";
+
+function GoogieSpell(img_dir, server_url) {
+ var cookie_value;
+ var lang;
+ cookie_value = getCookie('language');
+
+ if(cookie_value != null)
+ GOOGIE_CUR_LANG = cookie_value;
+
+ this.img_dir = img_dir;
+ this.server_url = server_url;
+
+ this.lang_to_word = {"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"};
+ this.langlist_codes = AJS.keys(this.lang_to_word);
+
+ this.show_change_lang_pic = true;
+
+ this.lang_state_observer = null;
+
+ this.spelling_state_observer = null;
+
+ this.request = null;
+ this.error_window = null;
+ this.language_window = null;
+ this.edit_layer = null;
+ this.orginal_text = null;
+ this.results = null;
+ this.text_area = null;
+ this.gselm = null;
+ this.ta_scroll_top = 0;
+ this.el_scroll_top = 0;
+
+ this.lang_chck_spell = "Check spelling";
+ this.lang_rsm_edt = "Resume editing";
+ this.lang_close = "Close";
+ this.lang_no_error_found = "No spelling errors found";
+ this.lang_revert = "Revert to";
+ this.show_spell_img = false; // modified by roundcube
+}
+
+GoogieSpell.prototype.setStateChanged = function(current_state) {
+ if(this.spelling_state_observer != null)
+ this.spelling_state_observer(current_state);
+}
+
+GoogieSpell.item_onmouseover = function(e) {
+ var elm = GoogieSpell.getEventElm(e);
+ if(elm.className != "googie_list_close" && elm.className != "googie_list_revert")
+ elm.className = "googie_list_onhover";
+ else
+ elm.parentNode.className = "googie_list_onhover";
+}
+
+GoogieSpell.item_onmouseout = function(e) {
+ var elm = GoogieSpell.getEventElm(e);
+ if(elm.className != "googie_list_close" && elm.className != "googie_list_revert")
+ elm.className = "googie_list_onout";
+ else
+ elm.parentNode.className = "googie_list_onout";
+}
+
+GoogieSpell.prototype.getGoogleUrl = function() {
+ return this.server_url + GOOGIE_CUR_LANG;
+}
+
+GoogieSpell.prototype.spellCheck = function(elm, name) {
+ this.ta_scroll_top = this.text_area.scrollTop;
+
+ this.appendIndicator(elm);
+
+ try {
+ this.hideLangWindow();
+ }
+ catch(e) {}
+
+ this.gselm = elm;
+
+ this.createEditLayer(this.text_area.offsetWidth, this.text_area.offsetHeight);
+
+ this.createErrorWindow();
+ AJS.getBody().appendChild(this.error_window);
+
+ try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); }
+ catch (e) { }
+
+ this.gselm.onclick = null;
+
+ this.orginal_text = this.text_area.value;
+ var me = this;
+
+ //Create request
+ var d = AJS.getRequest(this.getGoogleUrl());
+ var reqdone = function(req) {
+ var r_text = req.responseText;
+ if(r_text.match(/<c.*>/) != null) {
+ var results = GoogieSpell.parseResult(r_text);
+ //Before parsing be sure that errors were found
+ me.results = results;
+ me.showErrorsInIframe(results);
+ me.resumeEditingState();
+ }
+ else {
+ me.flashNoSpellingErrorState();
+ }
+ me.removeIndicator();
+ };
+
+ var reqfailed = function(req) {
+ alert("An error was encountered on the server. Please try again later.");
+ AJS.removeElement(me.gselm);
+ me.checkSpellingState();
+ me.removeIndicator();
+ };
+
+ d.addCallback(reqdone);
+ d.addErrback(reqfailed);
+
+ var req_text = GoogieSpell.escapeSepcial(this.orginal_text);
+ d.sendReq(GoogieSpell.createXMLReq(req_text));
+}
+
+GoogieSpell.escapeSepcial = function(val) {
+ return val.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
+}
+
+GoogieSpell.createXMLReq = function (text) {
+ return '<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>' + text + '</text></spellrequest>';
+}
+
+//Retunrs an array
+//result[item] -> ['attrs']
+// ['suggestions']
+GoogieSpell.parseResult = function(r_text) {
+ var re_split_attr_c = /\w="\d+"/g;
+ var re_split_text = /\t/g;
+
+ var matched_c = r_text.match(/<c[^>]*>[^<]*<\/c>/g);
+ var results = new Array();
+
+ for(var i=0; i < matched_c.length; i++) {
+ var item = new Array();
+
+ //Get attributes
+ item['attrs'] = new Array();
+ var split_c = matched_c[i].match(re_split_attr_c);
+ for(var j=0; j < split_c.length; j++) {
+ var c_attr = split_c[j].split(/=/);
+ item['attrs'][c_attr[0]] = parseInt(c_attr[1].replace('"', ''));
+ }
+
+ //Get suggestions
+ item['suggestions'] = new Array();
+ var only_text = matched_c[i].replace(/<[^>]*>/g, "");
+ var split_t = only_text.split(re_split_text);
+ for(var k=0; k < split_t.length; k++) {
+ if(split_t[k] != "")
+ item['suggestions'].push(split_t[k]);
+ }
+ results.push(item);
+ }
+ return results;
+}
+
+/****
+ Error window (the drop-down window)
+****/
+GoogieSpell.prototype.createErrorWindow = function() {
+ this.error_window = AJS.DIV();
+ this.error_window.className = "googie_window";
+}
+
+GoogieSpell.prototype.hideErrorWindow = function() {
+ this.error_window.style.visibility = "hidden";
+}
+
+GoogieSpell.prototype.updateOrginalText = function(offset, old_value, new_value, id) {
+ var part_1 = this.orginal_text.substring(0, offset);
+ var part_2 = this.orginal_text.substring(offset+old_value.length);
+ this.orginal_text = part_1 + new_value + part_2;
+ var add_2_offset = new_value.length - old_value.length;
+ for(var j=0; j < this.results.length; j++) {
+ //Don't edit the offset of the current item
+ if(j != id && j > id){
+ this.results[j]['attrs']['o'] += add_2_offset;
+ }
+ }
+}
+
+GoogieSpell.prototype.saveOldValue = function (id, old_value) {
+ this.results[id]['is_changed'] = true;
+ this.results[id]['old_value'] = old_value;
+}
+
+GoogieSpell.prototype.showErrorWindow = function(elm, id) {
+ var me = this;
+
+ var abs_pos = GoogieSpell.absolutePosition(elm);
+ abs_pos.y -= this.edit_layer.scrollTop;
+ this.error_window.style.visibility = "visible";
+ this.error_window.style.top = (abs_pos.y+20) + "px";
+ this.error_window.style.left = (abs_pos.x) + "px";
+ this.error_window.innerHTML = "";
+
+ //Build up the result list
+ var table = AJS.TABLE({'class': 'googie_list'});
+ var list = AJS.TBODY();
+
+ var suggestions = this.results[id]['suggestions'];
+ var offset = this.results[id]['attrs']['o'];
+ var len = this.results[id]['attrs']['l'];
+
+ if(suggestions.length == 0) {
+ var row = AJS.TR();
+ var item = AJS.TD();
+ var dummy = AJS.SPAN();
+ item.appendChild(AJS.TN("No suggestions :("));
+ row.appendChild(item);
+ list.appendChild(row);
+ }
+
+ for(i=0; i < suggestions.length; i++) {
+ var row = AJS.TR();
+ var item = AJS.TD();
+ var dummy = AJS.SPAN();
+ dummy.innerHTML = suggestions[i];
+ item.appendChild(AJS.TN(dummy.innerHTML));
+
+ item.onclick = function(e) {
+ var l_elm = GoogieSpell.getEventElm(e);
+ var old_value = elm.innerHTML;
+ var new_value = l_elm.innerHTML;
+
+ elm.style.color = "green";
+ elm.innerHTML = l_elm.innerHTML;
+ me.hideErrorWindow();
+
+ me.updateOrginalText(offset, old_value, new_value, id);
+
+ //Update to the new length
+ me.results[id]['attrs']['l'] = new_value.length;
+ me.saveOldValue(id, old_value);
+ };
+ item.onmouseover = GoogieSpell.item_onmouseover;
+ item.onmouseout = GoogieSpell.item_onmouseout;
+ row.appendChild(item);
+ list.appendChild(row);
+ }
+
+ //The element is changed, append the revert
+ if(this.results[id]['is_changed']) {
+ var old_value = this.results[id]['old_value'];
+ var offset = this.results[id]['attrs']['o'];
+ var revert_row = AJS.TR();
+ var revert = AJS.TD();
+
+ revert.onmouseover = GoogieSpell.item_onmouseover;
+ revert.onmouseout = GoogieSpell.item_onmouseout;
+ var rev_span = AJS.SPAN({'class': 'googie_list_revert'});
+ rev_span.innerHTML = this.lang_revert + " " + old_value;
+ revert.appendChild(rev_span);
+
+ revert.onclick = function(e) {
+ me.updateOrginalText(offset, elm.innerHTML, old_value, id);
+ elm.style.color = "#b91414";
+ elm.innerHTML = old_value;
+ me.hideErrorWindow();
+ };
+
+ revert_row.appendChild(revert);
+ list.appendChild(revert_row);
+ }
+
+ //Append the edit box
+ var edit_row = AJS.TR();
+ var edit = AJS.TD();
+
+ var edit_input = AJS.INPUT({'style': 'width: 120px; margin:0; padding:0'});
+
+ var onsub = function () {
+ if(edit_input.value != "") {
+ me.saveOldValue(id, elm.innerHTML);
+ me.updateOrginalText(offset, elm.innerHTML, edit_input.value, id);
+ elm.style.color = "green"
+ elm.innerHTML = edit_input.value;
+
+ me.hideErrorWindow();
+ return false;
+ }
+ };
+
+ var ok_pic = AJS.IMG({'src': this.img_dir + "ok.gif", 'style': 'width: 32px; height: 16px; margin-left: 2px; margin-right: 2px;'});
+ var edit_form = AJS.FORM({'style': 'margin: 0; padding: 0'}, edit_input, ok_pic);
+ ok_pic.onclick = onsub;
+ edit_form.onsubmit = onsub;
+
+ edit.appendChild(edit_form);
+ edit_row.appendChild(edit);
+ list.appendChild(edit_row);
+
+ //Close button
+ var close_row = AJS.TR();
+ var close = AJS.TD();
+
+ close.onmouseover = GoogieSpell.item_onmouseover;
+ close.onmouseout = GoogieSpell.item_onmouseout;
+
+ var spn_close = AJS.SPAN({'class': 'googie_list_close'});
+ spn_close.innerHTML = this.lang_close;
+ close.appendChild(spn_close);
+ close.onclick = function() { me.hideErrorWindow()};
+ close_row.appendChild(close);
+ list.appendChild(close_row);
+
+ table.appendChild(list);
+ this.error_window.appendChild(table);
+}
+
+
+/****
+ Edit layer (the layer where the suggestions are stored)
+****/
+GoogieSpell.prototype.createEditLayer = function(width, height) {
+ this.edit_layer = AJS.DIV({'class': 'googie_edit_layer'});
+
+ //Set the style so it looks like edit areas
+ this.edit_layer.className = this.text_area.className;
+ this.edit_layer.style.border = "1px solid #999";
+ this.edit_layer.style.overflow = "auto";
+ this.edit_layer.style.backgroundColor = "#F1EDFE";
+ this.edit_layer.style.padding = "3px";
+
+ this.edit_layer.style.width = (width-8) + "px";
+ this.edit_layer.style.height = height + "px";
+}
+
+GoogieSpell.prototype.resumeEditing = function(e, me) {
+ this.setStateChanged("check_spelling");
+ me.switch_lan_pic.style.display = "inline";
+
+ this.el_scroll_top = me.edit_layer.scrollTop;
+
+ var elm = GoogieSpell.getEventElm(e);
+ AJS.replaceChildNodes(elm, this.createSpellDiv());
+
+ elm.onclick = function(e) {
+ me.spellCheck(elm, me.text_area.id);
+ };
+ me.hideErrorWindow();
+
+ //Remove the EDIT_LAYER
+ me.edit_layer.parentNode.removeChild(me.edit_layer);
+
+ me.text_area.value = me.orginal_text;
+ AJS.showElement(me.text_area);
+ me.gselm.className = "googie_no_style";
+
+ me.text_area.scrollTop = this.el_scroll_top;
+
+ elm.onmouseout = null;
+}
+
+GoogieSpell.prototype.createErrorLink = function(text, id) {
+ var elm = AJS.SPAN({'class': 'googie_link'});
+ var me = this;
+ elm.onclick = function () {
+ me.showErrorWindow(elm, id);
+ };
+ elm.innerHTML = text;
+ return elm;
+}
+
+GoogieSpell.createPart = function(txt_part) {
+ if(txt_part == " ")
+ return AJS.TN(" ");
+ var result = AJS.SPAN();
+
+ var is_first = true;
+ var is_safari = (navigator.userAgent.toLowerCase().indexOf("safari") != -1);
+
+ var part = AJS.SPAN();
+ txt_part = GoogieSpell.escapeSepcial(txt_part);
+ txt_part = txt_part.replace(/\n/g, "<br>");
+ txt_part = txt_part.replace(/ /g, " ");
+ txt_part = txt_part.replace(/^ /g, " ");
+ txt_part = txt_part.replace(/ $/g, " ");
+
+ part.innerHTML = txt_part;
+
+ return part;
+}
+
+GoogieSpell.prototype.showErrorsInIframe = function(results) {
+ var output = AJS.DIV();
+ output.style.textAlign = "left";
+ var pointer = 0;
+ for(var i=0; i < results.length; i++) {
+ var offset = results[i]['attrs']['o'];
+ var len = results[i]['attrs']['l'];
+
+ var part_1_text = this.orginal_text.substring(pointer, offset);
+ var part_1 = GoogieSpell.createPart(part_1_text);
+ output.appendChild(part_1);
+ pointer += offset - pointer;
+
+ //If the last child was an error, then insert some space
+ output.appendChild(this.createErrorLink(this.orginal_text.substr(offset, len), i));
+ pointer += len;
+ }
+ //Insert the rest of the orginal text
+ var part_2_text = this.orginal_text.substr(pointer, this.orginal_text.length);
+
+ var part_2 = GoogieSpell.createPart(part_2_text);
+ output.appendChild(part_2);
+
+ this.edit_layer.appendChild(output);
+
+ //Hide text area
+ AJS.hideElement(this.text_area);
+ this.text_area.parentNode.insertBefore(this.edit_layer, this.text_area.nextSibling);
+ this.edit_layer.scrollTop = this.ta_scroll_top;
+}
+
+GoogieSpell.Position = function(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+//Get the absolute position of menu_slide
+GoogieSpell.absolutePosition = function(element) {
+ //Create a new object that has elements y and x pos...
+ var posObj = new GoogieSpell.Position(element.offsetLeft, element.offsetTop);
+
+ //Check if the element has an offsetParent - if it has .. loop until it has not
+ if(element.offsetParent) {
+ var temp_pos = GoogieSpell.absolutePosition(element.offsetParent);
+ posObj.x += temp_pos.x;
+ posObj.y += temp_pos.y;
+ }
+ return posObj;
+}
+
+GoogieSpell.getEventElm = function(e) {
+ var targ;
+ if (!e) var e = window.event;
+ if (e.target) targ = e.target;
+ else if (e.srcElement) targ = e.srcElement;
+ if (targ.nodeType == 3) // defeat Safari bug
+ targ = targ.parentNode;
+ return targ;
+}
+
+GoogieSpell.prototype.removeIndicator = function(elm) {
+ // modified by roundcube
+ if (window.rcube_webmail_client)
+ rcube_webmail_client.set_busy(false);
+ //AJS.removeElement(this.indicator);
+}
+
+GoogieSpell.prototype.appendIndicator = function(elm) {
+ // modified by roundcube
+ if (window.rcube_webmail_client)
+ rcube_webmail_client.set_busy(true, 'checking');
+/*
+ var img = AJS.IMG({'src': this.img_dir + 'indicator.gif', 'style': 'margin-right: 5px;'});
+ img.style.width = "16px";
+ img.style.height = "16px";
+ this.indicator = img;
+ img.style.textDecoration = "none";
+ AJS.insertBefore(img, elm);
+ */
+}
+
+/****
+ Choose language
+****/
+GoogieSpell.prototype.createLangWindow = function() {
+ this.language_window = AJS.DIV({'class': 'googie_window'});
+ this.language_window.style.width = "130px";
+
+ //Build up the result list
+ var table = AJS.TABLE({'class': 'googie_list'});
+ var list = AJS.TBODY();
+
+ this.lang_elms = new Array();
+
+ for(i=0; i < this.langlist_codes.length; i++) {
+ var row = AJS.TR();
+ var item = AJS.TD();
+ item.googieId = this.langlist_codes[i];
+ this.lang_elms.push(item);
+ var lang_span = AJS.SPAN();
+ lang_span.innerHTML = this.lang_to_word[this.langlist_codes[i]];
+ item.appendChild(AJS.TN(lang_span.innerHTML));
+
+ var me = this;
+
+ item.onclick = function(e) {
+ var elm = GoogieSpell.getEventElm(e);
+ me.deHighlightCurSel();
+
+ me.setCurrentLanguage(elm.googieId);
+
+ if(me.lang_state_observer != null) {
+ me.lang_state_observer();
+ }
+
+ me.highlightCurSel();
+ me.hideLangWindow();
+ };
+
+ item.onmouseover = function(e) {
+ var i_it = GoogieSpell.getEventElm(e);
+ if(i_it.className != "googie_list_selected")
+ i_it.className = "googie_list_onhover";
+ };
+ item.onmouseout = function(e) {
+ var i_it = GoogieSpell.getEventElm(e);
+ if(i_it.className != "googie_list_selected")
+ i_it.className = "googie_list_onout";
+ };
+
+ row.appendChild(item);
+ list.appendChild(row);
+ }
+
+ this.highlightCurSel();
+
+ //Close button
+ var close_row = AJS.TR();
+ var close = AJS.TD();
+ close.onmouseover = GoogieSpell.item_onmouseover;
+ close.onmouseout = GoogieSpell.item_onmouseout;
+ var spn_close = AJS.SPAN({'class': 'googie_list_close'});
+ spn_close.innerHTML = this.lang_close;
+ close.appendChild(spn_close);
+ var me = this;
+ close.onclick = function(e) {
+ me.hideLangWindow(); GoogieSpell.item_onmouseout(e);
+ };
+ close_row.appendChild(close);
+ list.appendChild(close_row);
+
+ table.appendChild(list);
+ this.language_window.appendChild(table);
+}
+
+GoogieSpell.prototype.setCurrentLanguage = function(lan_code) {
+ GOOGIE_CUR_LANG = lan_code;
+
+ //Set cookie
+ var now = new Date();
+ now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000);
+ setCookie('language', lan_code, now);
+}
+
+GoogieSpell.prototype.hideLangWindow = function() {
+ this.language_window.style.visibility = "hidden";
+ this.switch_lan_pic.className = "googie_lang_3d_on";
+}
+
+GoogieSpell.prototype.deHighlightCurSel = function() {
+ this.lang_cur_elm.className = "googie_list_onout";
+}
+
+GoogieSpell.prototype.highlightCurSel = function() {
+ for(var i=0; i < this.lang_elms.length; i++) {
+ if(this.lang_elms[i].googieId == GOOGIE_CUR_LANG) {
+ this.lang_elms[i].className = "googie_list_selected";
+ this.lang_cur_elm = this.lang_elms[i];
+ }
+ else {
+ this.lang_elms[i].className = "googie_list_onout";
+ }
+ }
+}
+
+GoogieSpell.prototype.showLangWindow = function(elm, ofst_top, ofst_left) {
+ if(!AJS.isDefined(ofst_top))
+ ofst_top = 20;
+ if(!AJS.isDefined(ofst_left))
+ ofst_left = 50;
+
+ this.createLangWindow();
+ AJS.getBody().appendChild(this.language_window);
+
+ var abs_pos = GoogieSpell.absolutePosition(elm);
+ AJS.showElement(this.language_window);
+ this.language_window.style.top = (abs_pos.y+ofst_top) + "px";
+ this.language_window.style.left = (abs_pos.x+ofst_left-this.language_window.offsetWidth) + "px";
+ this.highlightCurSel();
+ this.language_window.style.visibility = "visible";
+}
+
+GoogieSpell.prototype.flashNoSpellingErrorState = function() {
+ this.setStateChanged("no_error_found");
+ var me = this;
+ AJS.hideElement(this.switch_lan_pic);
+ this.gselm.innerHTML = this.lang_no_error_found;
+ this.gselm.className = "googie_check_spelling_ok";
+ this.gselm.style.textDecoration = "none";
+ this.gselm.style.cursor = "default";
+ var fu = function() {
+ AJS.removeElement(me.gselm);
+ me.checkSpellingState();
+ };
+ setTimeout(fu, 1000);
+}
+
+GoogieSpell.prototype.resumeEditingState = function() {
+ this.setStateChanged("resume_editing");
+ var me = this;
+ AJS.hideElement(me.switch_lan_pic);
+
+ //Change link text to resume
+ me.gselm.innerHTML = this.lang_rsm_edt;
+ me.gselm.onclick = function(e) {
+ me.resumeEditing(e, me);
+ }
+ me.gselm.className = "googie_check_spelling_ok";
+ me.edit_layer.scrollTop = me.ta_scroll_top;
+}
+
+GoogieSpell.prototype.createChangeLangPic = function() {
+ var switch_lan = AJS.A({'class': 'googie_lang_3d_on', 'style': 'padding-left: 6px;'}, AJS.IMG({'src': this.img_dir + 'change_lang.gif', 'alt': "Change language"}));
+ switch_lan.onmouseover = function() {
+ if(this.className != "googie_lang_3d_click")
+ this.className = "googie_lang_3d_on";
+ }
+
+ var me = this;
+ switch_lan.onclick = function() {
+ if(this.className == "googie_lang_3d_click") {
+ me.hideLangWindow();
+ }
+ else {
+ me.showLangWindow(switch_lan);
+ this.className = "googie_lang_3d_click";
+ }
+ }
+ return switch_lan;
+}
+
+GoogieSpell.prototype.createSpellDiv = function() {
+ var chk_spell = AJS.SPAN({'class': 'googie_check_spelling_link'});
+ chk_spell.innerHTML = this.lang_chck_spell;
+ var spell_img = null;
+ if(this.show_spell_img)
+ spell_img = AJS.IMG({'src': this.img_dir + "spellc.gif"});
+ return AJS.SPAN(spell_img, " ", chk_spell);
+}
+
+GoogieSpell.prototype.checkSpellingState = function() {
+ this.setStateChanged("check_spelling");
+ var me = this;
+ if(this.show_change_lang_pic)
+ this.switch_lan_pic = this.createChangeLangPic();
+ else
+ this.switch_lan_pic = AJS.SPAN();
+
+ var span_chck = this.createSpellDiv();
+ span_chck.onclick = function() {
+ me.spellCheck(span_chck);
+ }
+ AJS.appendChildNodes(this.spell_container, span_chck, " ", this.switch_lan_pic);
+ // modified by roundcube
+ this.check_link = span_chck;
+}
+
+GoogieSpell.prototype.setLanguages = function(lang_dict) {
+ this.lang_to_word = lang_dict;
+ this.langlist_codes = AJS.keys(lang_dict);
+}
+
+GoogieSpell.prototype.decorateTextarea = function(id, /*optional*/spell_container_id, force_width) {
+ var me = this;
+
+ if(typeof(id) == "string")
+ this.text_area = AJS.getElement(id);
+ else
+ this.text_area = id;
+
+ var r_width;
+
+ if(this.text_area != null) {
+ if(AJS.isDefined(spell_container_id)) {
+ if(typeof(spell_container_id) == "string")
+ this.spell_container = AJS.getElement(spell_container_id);
+ else
+ this.spell_container = spell_container_id;
+ }
+ else {
+ var table = AJS.TABLE();
+ var tbody = AJS.TBODY();
+ var tr = AJS.TR();
+ if(AJS.isDefined(force_width)) {
+ r_width = force_width;
+ }
+ else {
+ r_width = this.text_area.offsetWidth + "px";
+ }
+
+ var spell_container = AJS.TD();
+ this.spell_container = spell_container;
+
+ tr.appendChild(spell_container);
+
+ tbody.appendChild(tr);
+ table.appendChild(tbody);
+
+ AJS.insertBefore(table, this.text_area);
+
+ //Set width
+ table.style.width = '100%'; // modified by roundcube (old: r_width)
+ spell_container.style.width = r_width;
+ spell_container.style.textAlign = "right";
+ }
+
+ this.checkSpellingState();
+ }
+ else {
+ alert("Text area not found");
+ }
+}
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: SASL.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* Client implementation of various SASL mechanisms
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+require_once('PEAR.php');
+
+class Auth_SASL
+{
+ /**
+ * Factory class. Returns an object of the request
+ * type.
+ *
+ * @param string $type One of: Anonymous
+ * Plain
+ * CramMD5
+ * DigestMD5
+ * Types are not case sensitive
+ */
+ function &factory($type)
+ {
+ switch (strtolower($type)) {
+ case 'anonymous':
+ $filename = 'Auth/SASL/Anonymous.php';
+ $classname = 'Auth_SASL_Anonymous';
+ break;
+
+ case 'login':
+ $filename = 'Auth/SASL/Login.php';
+ $classname = 'Auth_SASL_Login';
+ break;
+
+ case 'plain':
+ $filename = 'Auth/SASL/Plain.php';
+ $classname = 'Auth_SASL_Plain';
+ break;
+
+ case 'crammd5':
+ $filename = 'Auth/SASL/CramMD5.php';
+ $classname = 'Auth_SASL_CramMD5';
+ break;
+
+ case 'digestmd5':
+ $filename = 'Auth/SASL/DigestMD5.php';
+ $classname = 'Auth_SASL_DigestMD5';
+ break;
+
+ default:
+ return PEAR::raiseError('Invalid SASL mechanism type');
+ break;
+ }
+
+ require_once($filename);
+ return new $classname();
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Anonymous.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* Implmentation of ANONYMOUS SASL mechanism
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+require_once('Auth/SASL/Common.php');
+
+class Auth_SASL_Anonymous extends Auth_SASL_Common
+{
+ /**
+ * Not much to do here except return the token supplied.
+ * No encoding, hashing or encryption takes place for this
+ * mechanism, simply one of:
+ * o An email address
+ * o An opaque string not containing "@" that can be interpreted
+ * by the sysadmin
+ * o Nothing
+ *
+ * We could have some logic here for the second option, but this
+ * would by no means create something interpretable.
+ *
+ * @param string $token Optional email address or string to provide
+ * as trace information.
+ * @return string The unaltered input token
+ */
+ function getResponse($token = '')
+ {
+ return $token;
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Common.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* Common functionality to SASL mechanisms
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+class Auth_SASL_Common
+{
+ /**
+ * Function which implements HMAC MD5 digest
+ *
+ * @param string $key The secret key
+ * @param string $data The data to protect
+ * @return string The HMAC MD5 digest
+ */
+ function _HMAC_MD5($key, $data)
+ {
+ if (strlen($key) > 64) {
+ $key = pack('H32', md5($key));
+ }
+
+ if (strlen($key) < 64) {
+ $key = str_pad($key, 64, chr(0));
+ }
+
+ $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
+ $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
+
+ $inner = pack('H32', md5($k_ipad . $data));
+ $digest = md5($k_opad . $inner);
+
+ return $digest;
+ }
+}
+?>
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: CramMD5.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* Implmentation of CRAM-MD5 SASL mechanism
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+require_once('Auth/SASL/Common.php');
+
+class Auth_SASL_CramMD5 extends Auth_SASL_Common
+{
+ /**
+ * Implements the CRAM-MD5 SASL mechanism
+ * This DOES NOT base64 encode the return value,
+ * you will need to do that yourself.
+ *
+ * @param string $user Username
+ * @param string $pass Password
+ * @param string $challenge The challenge supplied by the server.
+ * this should be already base64_decoded.
+ *
+ * @return string The string to pass back to the server, of the form
+ * "<user> <digest>". This is NOT base64_encoded.
+ */
+ function getResponse($user, $pass, $challenge)
+ {
+ return $user . ' ' . $this->_HMAC_MD5($pass, $challenge);
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: DigestMD5.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* Implmentation of DIGEST-MD5 SASL mechanism
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+require_once('Auth/SASL/Common.php');
+
+class Auth_SASL_DigestMD5 extends Auth_SASL_Common
+{
+ /**
+ * Provides the (main) client response for DIGEST-MD5
+ * requires a few extra parameters than the other
+ * mechanisms, which are unavoidable.
+ *
+ * @param string $authcid Authentication id (username)
+ * @param string $pass Password
+ * @param string $challenge The digest challenge sent by the server
+ * @param string $hostname The hostname of the machine you're connecting to
+ * @param string $service The servicename (eg. imap, pop, acap etc)
+ * @param string $authzid Authorization id (username to proxy as)
+ * @return string The digest response (NOT base64 encoded)
+ * @access public
+ */
+ function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '')
+ {
+ $challenge = $this->_parseChallenge($challenge);
+ $authzid_string = '';
+ if ($authzid != '') {
+ $authzid_string = ',authzid="' . $authzid . '"';
+ }
+
+ if (!empty($challenge)) {
+ $cnonce = $this->_getCnonce();
+ $digest_uri = sprintf('%s/%s', $service, $hostname);
+ $response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid);
+
+ return sprintf('username="%s",realm="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
+ } else {
+ return PEAR::raiseError('Invalid digest challenge');
+ }
+ }
+
+ /**
+ * Parses and verifies the digest challenge*
+ *
+ * @param string $challenge The digest challenge
+ * @return array The parsed challenge as an assoc
+ * array in the form "directive => value".
+ * @access private
+ */
+ function _parseChallenge($challenge)
+ {
+ $tokens = array();
+ while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) {
+
+ // Ignore these as per rfc2831
+ if ($matches[1] == 'opaque' OR $matches[1] == 'domain') {
+ $challenge = substr($challenge, strlen($matches[0]) + 1);
+ continue;
+ }
+
+ // Allowed multiple "realm" and "auth-param"
+ if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) {
+ if (is_array($tokens[$matches[1]])) {
+ $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
+ } else {
+ $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
+ }
+
+ // Any other multiple instance = failure
+ } elseif (!empty($tokens[$matches[1]])) {
+ $tokens = array();
+ break;
+
+ } else {
+ $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
+ }
+
+ // Remove the just parsed directive from the challenge
+ $challenge = substr($challenge, strlen($matches[0]) + 1);
+ }
+
+ /**
+ * Defaults and required directives
+ */
+ // Realm
+ if (empty($tokens['realm'])) {
+ $uname = posix_uname();
+ $tokens['realm'] = $uname['nodename'];
+ }
+
+ // Maxbuf
+ if (empty($tokens['maxbuf'])) {
+ $tokens['maxbuf'] = 65536;
+ }
+
+ // Required: nonce, algorithm
+ if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) {
+ return array();
+ }
+
+ return $tokens;
+ }
+
+ /**
+ * Creates the response= part of the digest response
+ *
+ * @param string $authcid Authentication id (username)
+ * @param string $pass Password
+ * @param string $realm Realm as provided by the server
+ * @param string $nonce Nonce as provided by the server
+ * @param string $cnonce Client nonce
+ * @param string $digest_uri The digest-uri= value part of the response
+ * @param string $authzid Authorization id
+ * @return string The response= part of the digest response
+ * @access private
+ */
+ function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '')
+ {
+ if ($authzid == '') {
+ $A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce);
+ } else {
+ $A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid);
+ }
+ $A2 = 'AUTHENTICATE:' . $digest_uri;
+ return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2)));
+ }
+
+ /**
+ * Creates the client nonce for the response
+ *
+ * @return string The cnonce value
+ * @access private
+ */
+ function _getCnonce()
+ {
+ if (file_exists('/dev/urandom')) {
+ return base64_encode(fread(fopen('/dev/urandom', 'r'), 32));
+
+ } elseif (file_exists('/dev/random')) {
+ return base64_encode(fread(fopen('/dev/random', 'r'), 32));
+
+ } else {
+ $str = '';
+ mt_srand((double)microtime()*10000000);
+ for ($i=0; $i<32; $i++) {
+ $str .= chr(mt_rand(0, 255));
+ }
+
+ return base64_encode($str);
+ }
+ }
+}
+?>
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Login.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* This is technically not a SASL mechanism, however
+* it's used by Net_Sieve, Net_Cyrus and potentially
+* other protocols , so here is a good place to abstract
+* it.
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+require_once('Auth/SASL/Common.php');
+
+class Auth_SASL_Login extends Auth_SASL_Common
+{
+ /**
+ * Pseudo SASL LOGIN mechanism
+ *
+ * @param string $user Username
+ * @param string $pass Password
+ * @return string LOGIN string
+ */
+ function getResponse($user, $pass)
+ {
+ return sprintf('LOGIN %s %s', $user, $pass);
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@php.net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Plain.php 17 2005-10-03 20:25:31Z roundcube $
+
+/**
+* Implmentation of PLAIN SASL mechanism
+*
+* @author Richard Heyes <richard@php.net>
+* @access public
+* @version 1.0
+* @package Auth_SASL
+*/
+
+require_once('Auth/SASL/Common.php');
+
+class Auth_SASL_Plain extends Auth_SASL_Common
+{
+ /**
+ * Returns PLAIN response
+ *
+ * @param string $authcid Authentication id (username)
+ * @param string $pass Password
+ * @param string $authzid Autorization id
+ * @return string PLAIN Response
+ */
+ function getResponse($authcid, $pass, $authzid = '')
+ {
+ return $authzid . chr(0) . $authcid . chr(0) . $pass;
+ }
+}
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Database independent query interface
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: DB.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the PEAR class so it can be extended from
+ */
+require_once 'PEAR.php';
+
+
+// {{{ constants
+// {{{ error codes
+
+/**#@+
+ * One of PEAR DB's portable error codes.
+ * @see DB_common::errorCode(), DB::errorMessage()
+ *
+ * {@internal If you add an error code here, make sure you also add a textual
+ * version of it in DB::errorMessage().}}
+ */
+
+/**
+ * The code returned by many methods upon success
+ */
+define('DB_OK', 1);
+
+/**
+ * Unkown error
+ */
+define('DB_ERROR', -1);
+
+/**
+ * Syntax error
+ */
+define('DB_ERROR_SYNTAX', -2);
+
+/**
+ * Tried to insert a duplicate value into a primary or unique index
+ */
+define('DB_ERROR_CONSTRAINT', -3);
+
+/**
+ * An identifier in the query refers to a non-existant object
+ */
+define('DB_ERROR_NOT_FOUND', -4);
+
+/**
+ * Tried to create a duplicate object
+ */
+define('DB_ERROR_ALREADY_EXISTS', -5);
+
+/**
+ * The current driver does not support the action you attempted
+ */
+define('DB_ERROR_UNSUPPORTED', -6);
+
+/**
+ * The number of parameters does not match the number of placeholders
+ */
+define('DB_ERROR_MISMATCH', -7);
+
+/**
+ * A literal submitted did not match the data type expected
+ */
+define('DB_ERROR_INVALID', -8);
+
+/**
+ * The current DBMS does not support the action you attempted
+ */
+define('DB_ERROR_NOT_CAPABLE', -9);
+
+/**
+ * A literal submitted was too long so the end of it was removed
+ */
+define('DB_ERROR_TRUNCATED', -10);
+
+/**
+ * A literal number submitted did not match the data type expected
+ */
+define('DB_ERROR_INVALID_NUMBER', -11);
+
+/**
+ * A literal date submitted did not match the data type expected
+ */
+define('DB_ERROR_INVALID_DATE', -12);
+
+/**
+ * Attempt to divide something by zero
+ */
+define('DB_ERROR_DIVZERO', -13);
+
+/**
+ * A database needs to be selected
+ */
+define('DB_ERROR_NODBSELECTED', -14);
+
+/**
+ * Could not create the object requested
+ */
+define('DB_ERROR_CANNOT_CREATE', -15);
+
+/**
+ * Could not drop the database requested because it does not exist
+ */
+define('DB_ERROR_CANNOT_DROP', -17);
+
+/**
+ * An identifier in the query refers to a non-existant table
+ */
+define('DB_ERROR_NOSUCHTABLE', -18);
+
+/**
+ * An identifier in the query refers to a non-existant column
+ */
+define('DB_ERROR_NOSUCHFIELD', -19);
+
+/**
+ * The data submitted to the method was inappropriate
+ */
+define('DB_ERROR_NEED_MORE_DATA', -20);
+
+/**
+ * The attempt to lock the table failed
+ */
+define('DB_ERROR_NOT_LOCKED', -21);
+
+/**
+ * The number of columns doesn't match the number of values
+ */
+define('DB_ERROR_VALUE_COUNT_ON_ROW', -22);
+
+/**
+ * The DSN submitted has problems
+ */
+define('DB_ERROR_INVALID_DSN', -23);
+
+/**
+ * Could not connect to the database
+ */
+define('DB_ERROR_CONNECT_FAILED', -24);
+
+/**
+ * The PHP extension needed for this DBMS could not be found
+ */
+define('DB_ERROR_EXTENSION_NOT_FOUND',-25);
+
+/**
+ * The present user has inadequate permissions to perform the task requestd
+ */
+define('DB_ERROR_ACCESS_VIOLATION', -26);
+
+/**
+ * The database requested does not exist
+ */
+define('DB_ERROR_NOSUCHDB', -27);
+
+/**
+ * Tried to insert a null value into a column that doesn't allow nulls
+ */
+define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
+/**#@-*/
+
+
+// }}}
+// {{{ prepared statement-related
+
+
+/**#@+
+ * Identifiers for the placeholders used in prepared statements.
+ * @see DB_common::prepare()
+ */
+
+/**
+ * Indicates a scalar (<kbd>?</kbd>) placeholder was used
+ *
+ * Quote and escape the value as necessary.
+ */
+define('DB_PARAM_SCALAR', 1);
+
+/**
+ * Indicates an opaque (<kbd>&</kbd>) placeholder was used
+ *
+ * The value presented is a file name. Extract the contents of that file
+ * and place them in this column.
+ */
+define('DB_PARAM_OPAQUE', 2);
+
+/**
+ * Indicates a misc (<kbd>!</kbd>) placeholder was used
+ *
+ * The value should not be quoted or escaped.
+ */
+define('DB_PARAM_MISC', 3);
+/**#@-*/
+
+
+// }}}
+// {{{ binary data-related
+
+
+/**#@+
+ * The different ways of returning binary data from queries.
+ */
+
+/**
+ * Sends the fetched data straight through to output
+ */
+define('DB_BINMODE_PASSTHRU', 1);
+
+/**
+ * Lets you return data as usual
+ */
+define('DB_BINMODE_RETURN', 2);
+
+/**
+ * Converts the data to hex format before returning it
+ *
+ * For example the string "123" would become "313233".
+ */
+define('DB_BINMODE_CONVERT', 3);
+/**#@-*/
+
+
+// }}}
+// {{{ fetch modes
+
+
+/**#@+
+ * Fetch Modes.
+ * @see DB_common::setFetchMode()
+ */
+
+/**
+ * Indicates the current default fetch mode should be used
+ * @see DB_common::$fetchmode
+ */
+define('DB_FETCHMODE_DEFAULT', 0);
+
+/**
+ * Column data indexed by numbers, ordered from 0 and up
+ */
+define('DB_FETCHMODE_ORDERED', 1);
+
+/**
+ * Column data indexed by column names
+ */
+define('DB_FETCHMODE_ASSOC', 2);
+
+/**
+ * Column data as object properties
+ */
+define('DB_FETCHMODE_OBJECT', 3);
+
+/**
+ * For multi-dimensional results, make the column name the first level
+ * of the array and put the row number in the second level of the array
+ *
+ * This is flipped from the normal behavior, which puts the row numbers
+ * in the first level of the array and the column names in the second level.
+ */
+define('DB_FETCHMODE_FLIPPED', 4);
+/**#@-*/
+
+/**#@+
+ * Old fetch modes. Left here for compatibility.
+ */
+define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
+define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC);
+define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
+/**#@-*/
+
+
+// }}}
+// {{{ tableInfo() && autoPrepare()-related
+
+
+/**#@+
+ * The type of information to return from the tableInfo() method.
+ *
+ * Bitwised constants, so they can be combined using <kbd>|</kbd>
+ * and removed using <kbd>^</kbd>.
+ *
+ * @see DB_common::tableInfo()
+ *
+ * {@internal Since the TABLEINFO constants are bitwised, if more of them are
+ * added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}}
+ */
+define('DB_TABLEINFO_ORDER', 1);
+define('DB_TABLEINFO_ORDERTABLE', 2);
+define('DB_TABLEINFO_FULL', 3);
+/**#@-*/
+
+
+/**#@+
+ * The type of query to create with the automatic query building methods.
+ * @see DB_common::autoPrepare(), DB_common::autoExecute()
+ */
+define('DB_AUTOQUERY_INSERT', 1);
+define('DB_AUTOQUERY_UPDATE', 2);
+/**#@-*/
+
+
+// }}}
+// {{{ portability modes
+
+
+/**#@+
+ * Portability Modes.
+ *
+ * Bitwised constants, so they can be combined using <kbd>|</kbd>
+ * and removed using <kbd>^</kbd>.
+ *
+ * @see DB_common::setOption()
+ *
+ * {@internal Since the PORTABILITY constants are bitwised, if more of them are
+ * added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}}
+ */
+
+/**
+ * Turn off all portability features
+ */
+define('DB_PORTABILITY_NONE', 0);
+
+/**
+ * Convert names of tables and fields to lower case
+ * when using the get*(), fetch*() and tableInfo() methods
+ */
+define('DB_PORTABILITY_LOWERCASE', 1);
+
+/**
+ * Right trim the data output by get*() and fetch*()
+ */
+define('DB_PORTABILITY_RTRIM', 2);
+
+/**
+ * Force reporting the number of rows deleted
+ */
+define('DB_PORTABILITY_DELETE_COUNT', 4);
+
+/**
+ * Enable hack that makes numRows() work in Oracle
+ */
+define('DB_PORTABILITY_NUMROWS', 8);
+
+/**
+ * Makes certain error messages in certain drivers compatible
+ * with those from other DBMS's
+ *
+ * + mysql, mysqli: change unique/primary key constraints
+ * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
+ *
+ * + odbc(access): MS's ODBC driver reports 'no such field' as code
+ * 07001, which means 'too few parameters.' When this option is on
+ * that code gets mapped to DB_ERROR_NOSUCHFIELD.
+ */
+define('DB_PORTABILITY_ERRORS', 16);
+
+/**
+ * Convert null values to empty strings in data output by
+ * get*() and fetch*()
+ */
+define('DB_PORTABILITY_NULL_TO_EMPTY', 32);
+
+/**
+ * Turn on all portability features
+ */
+define('DB_PORTABILITY_ALL', 63);
+/**#@-*/
+
+// }}}
+
+
+// }}}
+// {{{ class DB
+
+/**
+ * Database independent query interface
+ *
+ * The main "DB" class is simply a container class with some static
+ * methods for creating DB objects as well as some utility functions
+ * common to all parts of DB.
+ *
+ * The object model of DB is as follows (indentation means inheritance):
+ * <pre>
+ * DB The main DB class. This is simply a utility class
+ * with some "static" methods for creating DB objects as
+ * well as common utility functions for other DB classes.
+ *
+ * DB_common The base for each DB implementation. Provides default
+ * | implementations (in OO lingo virtual methods) for
+ * | the actual DB implementations as well as a bunch of
+ * | query utility functions.
+ * |
+ * +-DB_mysql The DB implementation for MySQL. Inherits DB_common.
+ * When calling DB::factory or DB::connect for MySQL
+ * connections, the object returned is an instance of this
+ * class.
+ * </pre>
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB
+{
+ // {{{ &factory()
+
+ /**
+ * Create a new DB object for the specified database type but don't
+ * connect to the database
+ *
+ * @param string $type the database type (eg "mysql")
+ * @param array $options an associative array of option names and values
+ *
+ * @return object a new DB object. A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function &factory($type, $options = false)
+ {
+ if (!is_array($options)) {
+ $options = array('persistent' => $options);
+ }
+
+ if (isset($options['debug']) && $options['debug'] >= 2) {
+ // expose php errors with sufficient debug level
+ include_once "DB/{$type}.php";
+ } else {
+ @include_once "DB/{$type}.php";
+ }
+
+ $classname = "DB_${type}";
+
+ if (!class_exists($classname)) {
+ $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
+ "Unable to include the DB/{$type}.php"
+ . " file for '$dsn'",
+ 'DB_Error', true);
+ return $tmp;
+ }
+
+ @$obj =& new $classname;
+
+ foreach ($options as $option => $value) {
+ $test = $obj->setOption($option, $value);
+ if (DB::isError($test)) {
+ return $test;
+ }
+ }
+
+ return $obj;
+ }
+
+ // }}}
+ // {{{ &connect()
+
+ /**
+ * Create a new DB object including a connection to the specified database
+ *
+ * Example 1.
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = 'pgsql://user:password@host/database';
+ * $options = array(
+ * 'debug' => 2,
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param mixed $dsn the string "data source name" or array in the
+ * format returned by DB::parseDSN()
+ * @param array $options an associative array of option names and values
+ *
+ * @return object a new DB object. A DB_Error object on failure.
+ *
+ * @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(),
+ * DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(),
+ * DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(),
+ * DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(),
+ * DB_sybase::connect()
+ *
+ * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError()
+ */
+ function &connect($dsn, $options = array())
+ {
+ $dsninfo = DB::parseDSN($dsn);
+ $type = $dsninfo['phptype'];
+
+ if (!is_array($options)) {
+ /*
+ * For backwards compatibility. $options used to be boolean,
+ * indicating whether the connection should be persistent.
+ */
+ $options = array('persistent' => $options);
+ }
+
+ if (isset($options['debug']) && $options['debug'] >= 2) {
+ // expose php errors with sufficient debug level
+ include_once "DB/${type}.php";
+ } else {
+ @include_once "DB/${type}.php";
+ }
+
+ $classname = "DB_${type}";
+ if (!class_exists($classname)) {
+ $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
+ "Unable to include the DB/{$type}.php"
+ . " file for '$dsn'",
+ 'DB_Error', true);
+ return $tmp;
+ }
+
+ @$obj =& new $classname;
+
+ foreach ($options as $option => $value) {
+ $test = $obj->setOption($option, $value);
+ if (DB::isError($test)) {
+ return $test;
+ }
+ }
+
+ $err = $obj->connect($dsninfo, $obj->getOption('persistent'));
+ if (DB::isError($err)) {
+ $err->addUserInfo($dsn);
+ return $err;
+ }
+
+ return $obj;
+ }
+
+ // }}}
+ // {{{ apiVersion()
+
+ /**
+ * Return the DB API version
+ *
+ * @return string the DB API version number
+ */
+ function apiVersion()
+ {
+ return '@package_version@';
+ }
+
+ // }}}
+ // {{{ isError()
+
+ /**
+ * Determines if a variable is a DB_Error object
+ *
+ * @param mixed $value the variable to check
+ *
+ * @return bool whether $value is DB_Error object
+ */
+ function isError($value)
+ {
+ return is_a($value, 'DB_Error');
+ }
+
+ // }}}
+ // {{{ isConnection()
+
+ /**
+ * Determines if a value is a DB_<driver> object
+ *
+ * @param mixed $value the value to test
+ *
+ * @return bool whether $value is a DB_<driver> object
+ */
+ function isConnection($value)
+ {
+ return (is_object($value) &&
+ is_subclass_of($value, 'db_common') &&
+ method_exists($value, 'simpleQuery'));
+ }
+
+ // }}}
+ // {{{ isManip()
+
+ /**
+ * Tell whether a query is a data manipulation or data definition query
+ *
+ * Examples of data manipulation queries are INSERT, UPDATE and DELETE.
+ * Examples of data definition queries are CREATE, DROP, ALTER, GRANT,
+ * REVOKE.
+ *
+ * @param string $query the query
+ *
+ * @return boolean whether $query is a data manipulation query
+ */
+ function isManip($query)
+ {
+ $manips = 'INSERT|UPDATE|DELETE|REPLACE|'
+ . 'CREATE|DROP|'
+ . 'LOAD DATA|SELECT .* INTO|COPY|'
+ . 'ALTER|GRANT|REVOKE|'
+ . 'LOCK|UNLOCK';
+ if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) {
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ errorMessage()
+
+ /**
+ * Return a textual error message for a DB error code
+ *
+ * @param integer $value the DB error code
+ *
+ * @return string the error message or false if the error code was
+ * not recognized
+ */
+ function errorMessage($value)
+ {
+ static $errorMessages;
+ if (!isset($errorMessages)) {
+ $errorMessages = array(
+ DB_ERROR => 'unknown error',
+ DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
+ DB_ERROR_ALREADY_EXISTS => 'already exists',
+ DB_ERROR_CANNOT_CREATE => 'can not create',
+ DB_ERROR_CANNOT_DROP => 'can not drop',
+ DB_ERROR_CONNECT_FAILED => 'connect failed',
+ DB_ERROR_CONSTRAINT => 'constraint violation',
+ DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
+ DB_ERROR_DIVZERO => 'division by zero',
+ DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
+ DB_ERROR_INVALID => 'invalid',
+ DB_ERROR_INVALID_DATE => 'invalid date or time',
+ DB_ERROR_INVALID_DSN => 'invalid DSN',
+ DB_ERROR_INVALID_NUMBER => 'invalid number',
+ DB_ERROR_MISMATCH => 'mismatch',
+ DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
+ DB_ERROR_NODBSELECTED => 'no database selected',
+ DB_ERROR_NOSUCHDB => 'no such database',
+ DB_ERROR_NOSUCHFIELD => 'no such field',
+ DB_ERROR_NOSUCHTABLE => 'no such table',
+ DB_ERROR_NOT_CAPABLE => 'DB backend not capable',
+ DB_ERROR_NOT_FOUND => 'not found',
+ DB_ERROR_NOT_LOCKED => 'not locked',
+ DB_ERROR_SYNTAX => 'syntax error',
+ DB_ERROR_UNSUPPORTED => 'not supported',
+ DB_ERROR_TRUNCATED => 'truncated',
+ DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
+ DB_OK => 'no error',
+ );
+ }
+
+ if (DB::isError($value)) {
+ $value = $value->getCode();
+ }
+
+ return isset($errorMessages[$value]) ? $errorMessages[$value]
+ : $errorMessages[DB_ERROR];
+ }
+
+ // }}}
+ // {{{ parseDSN()
+
+ /**
+ * Parse a data source name
+ *
+ * Additional keys can be added by appending a URI query string to the
+ * end of the DSN.
+ *
+ * The format of the supplied DSN is in its fullest form:
+ * <code>
+ * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
+ * </code>
+ *
+ * Most variations are allowed:
+ * <code>
+ * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
+ * phptype://username:password@hostspec/database_name
+ * phptype://username:password@hostspec
+ * phptype://username@hostspec
+ * phptype://hostspec/database
+ * phptype://hostspec
+ * phptype(dbsyntax)
+ * phptype
+ * </code>
+ *
+ * @param string $dsn Data Source Name to be parsed
+ *
+ * @return array an associative array with the following keys:
+ * + phptype: Database backend used in PHP (mysql, odbc etc.)
+ * + dbsyntax: Database used with regards to SQL syntax etc.
+ * + protocol: Communication protocol to use (tcp, unix etc.)
+ * + hostspec: Host specification (hostname[:port])
+ * + database: Database to use on the DBMS server
+ * + username: User name for login
+ * + password: Password for login
+ */
+ function parseDSN($dsn)
+ {
+ $parsed = array(
+ 'phptype' => false,
+ 'dbsyntax' => false,
+ 'username' => false,
+ 'password' => false,
+ 'protocol' => false,
+ 'hostspec' => false,
+ 'port' => false,
+ 'socket' => false,
+ 'database' => false,
+ );
+
+ if (is_array($dsn)) {
+ $dsn = array_merge($parsed, $dsn);
+ if (!$dsn['dbsyntax']) {
+ $dsn['dbsyntax'] = $dsn['phptype'];
+ }
+ return $dsn;
+ }
+
+ // Find phptype and dbsyntax
+ if (($pos = strpos($dsn, '://')) !== false) {
+ $str = substr($dsn, 0, $pos);
+ $dsn = substr($dsn, $pos + 3);
+ } else {
+ $str = $dsn;
+ $dsn = null;
+ }
+
+ // Get phptype and dbsyntax
+ // $str => phptype(dbsyntax)
+ if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
+ $parsed['phptype'] = $arr[1];
+ $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
+ } else {
+ $parsed['phptype'] = $str;
+ $parsed['dbsyntax'] = $str;
+ }
+
+ if (!count($dsn)) {
+ return $parsed;
+ }
+
+ // Get (if found): username and password
+ // $dsn => username:password@protocol+hostspec/database
+ if (($at = strrpos($dsn,'@')) !== false) {
+ $str = substr($dsn, 0, $at);
+ $dsn = substr($dsn, $at + 1);
+ if (($pos = strpos($str, ':')) !== false) {
+ $parsed['username'] = rawurldecode(substr($str, 0, $pos));
+ $parsed['password'] = rawurldecode(substr($str, $pos + 1));
+ } else {
+ $parsed['username'] = rawurldecode($str);
+ }
+ }
+
+ // Find protocol and hostspec
+
+ if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
+ // $dsn => proto(proto_opts)/database
+ $proto = $match[1];
+ $proto_opts = $match[2] ? $match[2] : false;
+ $dsn = $match[3];
+
+ } else {
+ // $dsn => protocol+hostspec/database (old format)
+ if (strpos($dsn, '+') !== false) {
+ list($proto, $dsn) = explode('+', $dsn, 2);
+ }
+ if (strpos($dsn, '/') !== false) {
+ list($proto_opts, $dsn) = explode('/', $dsn, 2);
+ } else {
+ $proto_opts = $dsn;
+ $dsn = null;
+ }
+ }
+
+ // process the different protocol options
+ $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
+ $proto_opts = rawurldecode($proto_opts);
+ if ($parsed['protocol'] == 'tcp') {
+ if (strpos($proto_opts, ':') !== false) {
+ list($parsed['hostspec'],
+ $parsed['port']) = explode(':', $proto_opts);
+ } else {
+ $parsed['hostspec'] = $proto_opts;
+ }
+ } elseif ($parsed['protocol'] == 'unix') {
+ $parsed['socket'] = $proto_opts;
+ }
+
+ // Get dabase if any
+ // $dsn => database
+ if ($dsn) {
+ if (($pos = strpos($dsn, '?')) === false) {
+ // /database
+ $parsed['database'] = rawurldecode($dsn);
+ } else {
+ // /database?param1=value1¶m2=value2
+ $parsed['database'] = rawurldecode(substr($dsn, 0, $pos));
+ $dsn = substr($dsn, $pos + 1);
+ if (strpos($dsn, '&') !== false) {
+ $opts = explode('&', $dsn);
+ } else { // database?param1=value1
+ $opts = array($dsn);
+ }
+ foreach ($opts as $opt) {
+ list($key, $value) = explode('=', $opt);
+ if (!isset($parsed[$key])) {
+ // don't allow params overwrite
+ $parsed[$key] = rawurldecode($value);
+ }
+ }
+ }
+ }
+
+ return $parsed;
+ }
+
+ // }}}
+}
+
+// }}}
+// {{{ class DB_Error
+
+/**
+ * DB_Error implements a class for reporting portable database error
+ * messages
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_Error extends PEAR_Error
+{
+ // {{{ constructor
+
+ /**
+ * DB_Error constructor
+ *
+ * @param mixed $code DB error code, or string with error message
+ * @param int $mode what "error mode" to operate in
+ * @param int $level what error level to use for $mode &
+ * PEAR_ERROR_TRIGGER
+ * @param mixed $debuginfo additional debug info, such as the last query
+ *
+ * @see PEAR_Error
+ */
+ function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
+ $level = E_USER_NOTICE, $debuginfo = null)
+ {
+ if (is_int($code)) {
+ $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code,
+ $mode, $level, $debuginfo);
+ } else {
+ $this->PEAR_Error("DB Error: $code", DB_ERROR,
+ $mode, $level, $debuginfo);
+ }
+ }
+
+ // }}}
+}
+
+// }}}
+// {{{ class DB_result
+
+/**
+ * This class implements a wrapper for a DB result set
+ *
+ * A new instance of this class will be returned by the DB implementation
+ * after processing a query that returns data.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_result
+{
+ // {{{ properties
+
+ /**
+ * Should results be freed automatically when there are no more rows?
+ * @var boolean
+ * @see DB_common::$options
+ */
+ var $autofree;
+
+ /**
+ * A reference to the DB_<driver> object
+ * @var object
+ */
+ var $dbh;
+
+ /**
+ * The current default fetch mode
+ * @var integer
+ * @see DB_common::$fetchmode
+ */
+ var $fetchmode;
+
+ /**
+ * The name of the class into which results should be fetched when
+ * DB_FETCHMODE_OBJECT is in effect
+ *
+ * @var string
+ * @see DB_common::$fetchmode_object_class
+ */
+ var $fetchmode_object_class;
+
+ /**
+ * The number of rows to fetch from a limit query
+ * @var integer
+ */
+ var $limit_count = null;
+
+ /**
+ * The row to start fetching from in limit queries
+ * @var integer
+ */
+ var $limit_from = null;
+
+ /**
+ * The execute parameters that created this result
+ * @var array
+ * @since Property available since Release 1.7.0
+ */
+ var $parameters;
+
+ /**
+ * The query string that created this result
+ *
+ * Copied here incase it changes in $dbh, which is referenced
+ *
+ * @var string
+ * @since Property available since Release 1.7.0
+ */
+ var $query;
+
+ /**
+ * The query result resource id created by PHP
+ * @var resource
+ */
+ var $result;
+
+ /**
+ * The present row being dealt with
+ * @var integer
+ */
+ var $row_counter = null;
+
+ /**
+ * The prepared statement resource id created by PHP in $dbh
+ *
+ * This resource is only available when the result set was created using
+ * a driver's native execute() method, not PEAR DB's emulated one.
+ *
+ * Copied here incase it changes in $dbh, which is referenced
+ *
+ * {@internal Mainly here because the InterBase/Firebird API is only
+ * able to retrieve data from result sets if the statemnt handle is
+ * still in scope.}}
+ *
+ * @var resource
+ * @since Property available since Release 1.7.0
+ */
+ var $statement;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor sets the object's properties
+ *
+ * @param object &$dbh the DB object reference
+ * @param resource $result the result resource id
+ * @param array $options an associative array with result options
+ *
+ * @return void
+ */
+ function DB_result(&$dbh, $result, $options = array())
+ {
+ $this->autofree = $dbh->options['autofree'];
+ $this->dbh = &$dbh;
+ $this->fetchmode = $dbh->fetchmode;
+ $this->fetchmode_object_class = $dbh->fetchmode_object_class;
+ $this->parameters = $dbh->last_parameters;
+ $this->query = $dbh->last_query;
+ $this->result = $result;
+ $this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt;
+ foreach ($options as $key => $value) {
+ $this->setOption($key, $value);
+ }
+ }
+
+ /**
+ * Set options for the DB_result object
+ *
+ * @param string $key the option to set
+ * @param mixed $value the value to set the option to
+ *
+ * @return void
+ */
+ function setOption($key, $value = null)
+ {
+ switch ($key) {
+ case 'limit_from':
+ $this->limit_from = $value;
+ break;
+ case 'limit_count':
+ $this->limit_count = $value;
+ }
+ }
+
+ // }}}
+ // {{{ fetchRow()
+
+ /**
+ * Fetch a row of data and return it by reference into an array
+ *
+ * The type of array returned can be controlled either by setting this
+ * method's <var>$fetchmode</var> parameter or by changing the default
+ * fetch mode setFetchMode() before calling this method.
+ *
+ * There are two options for standardizing the information returned
+ * from databases, ensuring their values are consistent when changing
+ * DBMS's. These portability options can be turned on when creating a
+ * new DB object or by using setOption().
+ *
+ * + <var>DB_PORTABILITY_LOWERCASE</var>
+ * convert names of fields to lower case
+ *
+ * + <var>DB_PORTABILITY_RTRIM</var>
+ * right trim the data
+ *
+ * @param int $fetchmode the constant indicating how to format the data
+ * @param int $rownum the row number to fetch (index starts at 0)
+ *
+ * @return mixed an array or object containing the row's data,
+ * NULL when the end of the result set is reached
+ * or a DB_Error object on failure.
+ *
+ * @see DB_common::setOption(), DB_common::setFetchMode()
+ */
+ function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
+ {
+ if ($fetchmode === DB_FETCHMODE_DEFAULT) {
+ $fetchmode = $this->fetchmode;
+ }
+ if ($fetchmode === DB_FETCHMODE_OBJECT) {
+ $fetchmode = DB_FETCHMODE_ASSOC;
+ $object_class = $this->fetchmode_object_class;
+ }
+ if ($this->limit_from !== null) {
+ if ($this->row_counter === null) {
+ $this->row_counter = $this->limit_from;
+ // Skip rows
+ if ($this->dbh->features['limit'] === false) {
+ $i = 0;
+ while ($i++ < $this->limit_from) {
+ $this->dbh->fetchInto($this->result, $arr, $fetchmode);
+ }
+ }
+ }
+ if ($this->row_counter >= ($this->limit_from + $this->limit_count))
+ {
+ if ($this->autofree) {
+ $this->free();
+ }
+ $tmp = null;
+ return $tmp;
+ }
+ if ($this->dbh->features['limit'] === 'emulate') {
+ $rownum = $this->row_counter;
+ }
+ $this->row_counter++;
+ }
+ $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
+ if ($res === DB_OK) {
+ if (isset($object_class)) {
+ // The default mode is specified in the
+ // DB_common::fetchmode_object_class property
+ if ($object_class == 'stdClass') {
+ $arr = (object) $arr;
+ } else {
+ $arr = &new $object_class($arr);
+ }
+ }
+ return $arr;
+ }
+ if ($res == null && $this->autofree) {
+ $this->free();
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Fetch a row of data into an array which is passed by reference
+ *
+ * The type of array returned can be controlled either by setting this
+ * method's <var>$fetchmode</var> parameter or by changing the default
+ * fetch mode setFetchMode() before calling this method.
+ *
+ * There are two options for standardizing the information returned
+ * from databases, ensuring their values are consistent when changing
+ * DBMS's. These portability options can be turned on when creating a
+ * new DB object or by using setOption().
+ *
+ * + <var>DB_PORTABILITY_LOWERCASE</var>
+ * convert names of fields to lower case
+ *
+ * + <var>DB_PORTABILITY_RTRIM</var>
+ * right trim the data
+ *
+ * @param array &$arr the variable where the data should be placed
+ * @param int $fetchmode the constant indicating how to format the data
+ * @param int $rownum the row number to fetch (index starts at 0)
+ *
+ * @return mixed DB_OK if a row is processed, NULL when the end of the
+ * result set is reached or a DB_Error object on failure
+ *
+ * @see DB_common::setOption(), DB_common::setFetchMode()
+ */
+ function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
+ {
+ if ($fetchmode === DB_FETCHMODE_DEFAULT) {
+ $fetchmode = $this->fetchmode;
+ }
+ if ($fetchmode === DB_FETCHMODE_OBJECT) {
+ $fetchmode = DB_FETCHMODE_ASSOC;
+ $object_class = $this->fetchmode_object_class;
+ }
+ if ($this->limit_from !== null) {
+ if ($this->row_counter === null) {
+ $this->row_counter = $this->limit_from;
+ // Skip rows
+ if ($this->dbh->features['limit'] === false) {
+ $i = 0;
+ while ($i++ < $this->limit_from) {
+ $this->dbh->fetchInto($this->result, $arr, $fetchmode);
+ }
+ }
+ }
+ if ($this->row_counter >= (
+ $this->limit_from + $this->limit_count))
+ {
+ if ($this->autofree) {
+ $this->free();
+ }
+ return null;
+ }
+ if ($this->dbh->features['limit'] === 'emulate') {
+ $rownum = $this->row_counter;
+ }
+
+ $this->row_counter++;
+ }
+ $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
+ if ($res === DB_OK) {
+ if (isset($object_class)) {
+ // default mode specified in the
+ // DB_common::fetchmode_object_class property
+ if ($object_class == 'stdClass') {
+ $arr = (object) $arr;
+ } else {
+ $arr = new $object_class($arr);
+ }
+ }
+ return DB_OK;
+ }
+ if ($res == null && $this->autofree) {
+ $this->free();
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Get the the number of columns in a result set
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ */
+ function numCols()
+ {
+ return $this->dbh->numCols($this->result);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Get the number of rows in a result set
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function numRows()
+ {
+ if ($this->dbh->features['numrows'] === 'emulate'
+ && $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS)
+ {
+ if ($this->dbh->features['prepare']) {
+ $res = $this->dbh->query($this->query, $this->parameters);
+ } else {
+ $res = $this->dbh->query($this->query);
+ }
+ if (DB::isError($res)) {
+ return $res;
+ }
+ $i = 0;
+ while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) {
+ $i++;
+ }
+ return $i;
+ } else {
+ return $this->dbh->numRows($this->result);
+ }
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Get the next result if a batch of queries was executed
+ *
+ * @return bool true if a new result is available or false if not
+ */
+ function nextResult()
+ {
+ return $this->dbh->nextResult($this->result);
+ }
+
+ // }}}
+ // {{{ free()
+
+ /**
+ * Frees the resources allocated for this result set
+ *
+ * @return bool true on success. A DB_Error object on failure.
+ */
+ function free()
+ {
+ $err = $this->dbh->freeResult($this->result);
+ if (DB::isError($err)) {
+ return $err;
+ }
+ $this->result = false;
+ $this->statement = false;
+ return true;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * @see DB_common::tableInfo()
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function tableInfo($mode = null)
+ {
+ if (is_string($mode)) {
+ return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+ return $this->dbh->tableInfo($this, $mode);
+ }
+
+ // }}}
+ // {{{ getQuery()
+
+ /**
+ * Determine the query string that created this result
+ *
+ * @return string the query string
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function getQuery()
+ {
+ return $this->query;
+ }
+
+ // }}}
+ // {{{ getRowCounter()
+
+ /**
+ * Tells which row number is currently being processed
+ *
+ * @return integer the current row being looked at. Starts at 1.
+ */
+ function getRowCounter()
+ {
+ return $this->row_counter;
+ }
+
+ // }}}
+}
+
+// }}}
+// {{{ class DB_row
+
+/**
+ * PEAR DB Row Object
+ *
+ * The object contains a row of data from a result set. Each column's data
+ * is placed in a property named for the column.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ * @see DB_common::setFetchMode()
+ */
+class DB_row
+{
+ // {{{ constructor
+
+ /**
+ * The constructor places a row's data into properties of this object
+ *
+ * @param array the array containing the row's data
+ *
+ * @return void
+ */
+ function DB_row(&$arr)
+ {
+ foreach ($arr as $key => $value) {
+ $this->$key = &$arr[$key];
+ }
+ }
+
+ // }}}
+}
+
+// }}}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Contains the DB_common base class
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: common.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the PEAR class so it can be extended from
+ */
+require_once 'PEAR.php';
+
+/**
+ * DB_common is the base class from which each database driver class extends
+ *
+ * All common methods are declared here. If a given DBMS driver contains
+ * a particular method, that method will overload the one here.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_common extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * The current default fetch mode
+ * @var integer
+ */
+ var $fetchmode = DB_FETCHMODE_ORDERED;
+
+ /**
+ * The name of the class into which results should be fetched when
+ * DB_FETCHMODE_OBJECT is in effect
+ *
+ * @var string
+ */
+ var $fetchmode_object_class = 'stdClass';
+
+ /**
+ * Was a connection present when the object was serialized()?
+ * @var bool
+ * @see DB_common::__sleep(), DB_common::__wake()
+ */
+ var $was_connected = null;
+
+ /**
+ * The most recently executed query
+ * @var string
+ */
+ var $last_query = '';
+
+ /**
+ * Run-time configuration options
+ *
+ * The 'optimize' option has been deprecated. Use the 'portability'
+ * option instead.
+ *
+ * @var array
+ * @see DB_common::setOption()
+ */
+ var $options = array(
+ 'result_buffering' => 500,
+ 'persistent' => false,
+ 'ssl' => false,
+ 'debug' => 0,
+ 'seqname_format' => '%s_seq',
+ 'autofree' => false,
+ 'portability' => DB_PORTABILITY_NONE,
+ 'optimize' => 'performance', // Deprecated. Use 'portability'.
+ );
+
+ /**
+ * The parameters from the most recently executed query
+ * @var array
+ * @since Property available since Release 1.7.0
+ */
+ var $last_parameters = array();
+
+ /**
+ * The elements from each prepared statement
+ * @var array
+ */
+ var $prepare_tokens = array();
+
+ /**
+ * The data types of the various elements in each prepared statement
+ * @var array
+ */
+ var $prepare_types = array();
+
+ /**
+ * The prepared queries
+ * @var array
+ */
+ var $prepared_queries = array();
+
+
+ // }}}
+ // {{{ DB_common
+
+ /**
+ * This constructor calls <kbd>$this->PEAR('DB_Error')</kbd>
+ *
+ * @return void
+ */
+ function DB_common()
+ {
+ $this->PEAR('DB_Error');
+ }
+
+ // }}}
+ // {{{ __sleep()
+
+ /**
+ * Automatically indicates which properties should be saved
+ * when PHP's serialize() function is called
+ *
+ * @return array the array of properties names that should be saved
+ */
+ function __sleep()
+ {
+ if ($this->connection) {
+ // Don't disconnect(), people use serialize() for many reasons
+ $this->was_connected = true;
+ } else {
+ $this->was_connected = false;
+ }
+ if (isset($this->autocommit)) {
+ return array('autocommit',
+ 'dbsyntax',
+ 'dsn',
+ 'features',
+ 'fetchmode',
+ 'fetchmode_object_class',
+ 'options',
+ 'was_connected',
+ );
+ } else {
+ return array('dbsyntax',
+ 'dsn',
+ 'features',
+ 'fetchmode',
+ 'fetchmode_object_class',
+ 'options',
+ 'was_connected',
+ );
+ }
+ }
+
+ // }}}
+ // {{{ __wakeup()
+
+ /**
+ * Automatically reconnects to the database when PHP's unserialize()
+ * function is called
+ *
+ * The reconnection attempt is only performed if the object was connected
+ * at the time PHP's serialize() function was run.
+ *
+ * @return void
+ */
+ function __wakeup()
+ {
+ if ($this->was_connected) {
+ $this->connect($this->dsn, $this->options);
+ }
+ }
+
+ // }}}
+ // {{{ __toString()
+
+ /**
+ * Automatic string conversion for PHP 5
+ *
+ * @return string a string describing the current PEAR DB object
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function __toString()
+ {
+ $info = strtolower(get_class($this));
+ $info .= ': (phptype=' . $this->phptype .
+ ', dbsyntax=' . $this->dbsyntax .
+ ')';
+ if ($this->connection) {
+ $info .= ' [connected]';
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * DEPRECATED: String conversion method
+ *
+ * @return string a string describing the current PEAR DB object
+ *
+ * @deprecated Method deprecated in Release 1.7.0
+ */
+ function toString()
+ {
+ return $this->__toString();
+ }
+
+ // }}}
+ // {{{ quoteString()
+
+ /**
+ * DEPRECATED: Quotes a string so it can be safely used within string
+ * delimiters in a query
+ *
+ * @param string $string the string to be quoted
+ *
+ * @return string the quoted string
+ *
+ * @see DB_common::quoteSmart(), DB_common::escapeSimple()
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function quoteString($string)
+ {
+ $string = $this->quote($string);
+ if ($string{0} == "'") {
+ return substr($string, 1, -1);
+ }
+ return $string;
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * DEPRECATED: Quotes a string so it can be safely used in a query
+ *
+ * @param string $string the string to quote
+ *
+ * @return string the quoted string or the string <samp>NULL</samp>
+ * if the value submitted is <kbd>null</kbd>.
+ *
+ * @see DB_common::quoteSmart(), DB_common::escapeSimple()
+ * @deprecated Deprecated in release 1.6.0
+ */
+ function quote($string = null)
+ {
+ return ($string === null) ? 'NULL'
+ : "'" . str_replace("'", "''", $string) . "'";
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * Delimiting style depends on which database driver is being used.
+ *
+ * NOTE: just because you CAN use delimited identifiers doesn't mean
+ * you SHOULD use them. In general, they end up causing way more
+ * problems than they solve.
+ *
+ * Portability is broken by using the following characters inside
+ * delimited identifiers:
+ * + backtick (<kbd>`</kbd>) -- due to MySQL
+ * + double quote (<kbd>"</kbd>) -- due to Oracle
+ * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
+ *
+ * Delimited identifiers are known to generally work correctly under
+ * the following drivers:
+ * + mssql
+ * + mysql
+ * + mysqli
+ * + oci8
+ * + odbc(access)
+ * + odbc(db2)
+ * + pgsql
+ * + sqlite
+ * + sybase (must execute <kbd>set quoted_identifier on</kbd> sometime
+ * prior to use)
+ *
+ * InterBase doesn't seem to be able to use delimited identifiers
+ * via PHP 4. They work fine under PHP 5.
+ *
+ * @param string $str the identifier name to be quoted
+ *
+ * @return string the quoted identifier
+ *
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '"' . str_replace('"', '""', $str) . '"';
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * The output depends on the PHP data type of input and the database
+ * type being used.
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * <ul>
+ * <li>
+ * <kbd>input</kbd> -> <samp>returns</samp>
+ * </li>
+ * <li>
+ * <kbd>null</kbd> -> the string <samp>NULL</samp>
+ * </li>
+ * <li>
+ * <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
+ * </li>
+ * <li>
+ * <kbd>bool</kbd> -> output depends on the driver in use
+ * Most drivers return integers: <samp>1</samp> if
+ * <kbd>true</kbd> or <samp>0</samp> if
+ * <kbd>false</kbd>.
+ * Some return strings: <samp>TRUE</samp> if
+ * <kbd>true</kbd> or <samp>FALSE</samp> if
+ * <kbd>false</kbd>.
+ * Finally one returns strings: <samp>T</samp> if
+ * <kbd>true</kbd> or <samp>F</samp> if
+ * <kbd>false</kbd>. Here is a list of each DBMS,
+ * the values returned and the suggested column type:
+ * <ul>
+ * <li>
+ * <kbd>dbase</kbd> -> <samp>T/F</samp>
+ * (<kbd>Logical</kbd>)
+ * </li>
+ * <li>
+ * <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
+ * (<kbd>BOOLEAN</kbd>)
+ * </li>
+ * <li>
+ * <kbd>ibase</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>ifx</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>msql</kbd> -> <samp>1/0</samp>
+ * (<kbd>INTEGER</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mssql</kbd> -> <samp>1/0</samp>
+ * (<kbd>BIT</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mysql</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mysqli</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>oci8</kbd> -> <samp>1/0</samp>
+ * (<kbd>NUMBER(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>odbc</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
+ * (<kbd>BOOLEAN</kbd>)
+ * </li>
+ * <li>
+ * <kbd>sqlite</kbd> -> <samp>1/0</samp>
+ * (<kbd>INTEGER</kbd>)
+ * </li>
+ * <li>
+ * <kbd>sybase</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * </ul>
+ * [1] Accommodate the lowest common denominator because not all
+ * versions of have <kbd>BOOLEAN</kbd>.
+ * </li>
+ * <li>
+ * other (including strings and numeric strings) ->
+ * the data with single quotes escaped by preceeding
+ * single quotes, backslashes are escaped by preceeding
+ * backslashes, then the whole string is encapsulated
+ * between single quotes
+ * </li>
+ * </ul>
+ *
+ * @see DB_common::escapeSimple()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in) || is_double($in)) {
+ return $in;
+ } elseif (is_bool($in)) {
+ return $in ? 1 : 0;
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * In SQLite, this makes things safe for inserts/updates, but may
+ * cause problems when performing text comparisons against columns
+ * containing binary data. See the
+ * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return str_replace("'", "''", $str);
+ }
+
+ // }}}
+ // {{{ provides()
+
+ /**
+ * Tells whether the present driver supports a given feature
+ *
+ * @param string $feature the feature you're curious about
+ *
+ * @return bool whether this driver supports $feature
+ */
+ function provides($feature)
+ {
+ return $this->features[$feature];
+ }
+
+ // }}}
+ // {{{ setFetchMode()
+
+ /**
+ * Sets the fetch mode that should be used by default for query results
+ *
+ * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC
+ * or DB_FETCHMODE_OBJECT
+ * @param string $object_class the class name of the object to be returned
+ * by the fetch methods when the
+ * DB_FETCHMODE_OBJECT mode is selected.
+ * If no class is specified by default a cast
+ * to object from the assoc array row will be
+ * done. There is also the posibility to use
+ * and extend the 'DB_row' class.
+ *
+ * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
+ */
+ function setFetchMode($fetchmode, $object_class = 'stdClass')
+ {
+ switch ($fetchmode) {
+ case DB_FETCHMODE_OBJECT:
+ $this->fetchmode_object_class = $object_class;
+ case DB_FETCHMODE_ORDERED:
+ case DB_FETCHMODE_ASSOC:
+ $this->fetchmode = $fetchmode;
+ break;
+ default:
+ return $this->raiseError('invalid fetchmode mode');
+ }
+ }
+
+ // }}}
+ // {{{ setOption()
+
+ /**
+ * Sets run-time configuration options for PEAR DB
+ *
+ * Options, their data types, default values and description:
+ * <ul>
+ * <li>
+ * <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />should results be freed automatically when there are no
+ * more rows?
+ * </li><li>
+ * <var>result_buffering</var> <kbd>integer</kbd> = <samp>500</samp>
+ * <br />how many rows of the result set should be buffered?
+ * <br />In mysql: mysql_unbuffered_query() is used instead of
+ * mysql_query() if this value is 0. (Release 1.7.0)
+ * <br />In oci8: this value is passed to ocisetprefetch().
+ * (Release 1.7.0)
+ * </li><li>
+ * <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
+ * <br />debug level
+ * </li><li>
+ * <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />should the connection be persistent?
+ * </li><li>
+ * <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
+ * <br />portability mode constant (see below)
+ * </li><li>
+ * <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
+ * <br />the sprintf() format string used on sequence names. This
+ * format is applied to sequence names passed to
+ * createSequence(), nextID() and dropSequence().
+ * </li><li>
+ * <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />use ssl to connect?
+ * </li>
+ * </ul>
+ *
+ * -----------------------------------------
+ *
+ * PORTABILITY MODES
+ *
+ * These modes are bitwised, so they can be combined using <kbd>|</kbd>
+ * and removed using <kbd>^</kbd>. See the examples section below on how
+ * to do this.
+ *
+ * <samp>DB_PORTABILITY_NONE</samp>
+ * turn off all portability features
+ *
+ * This mode gets automatically turned on if the deprecated
+ * <var>optimize</var> option gets set to <samp>performance</samp>.
+ *
+ *
+ * <samp>DB_PORTABILITY_LOWERCASE</samp>
+ * convert names of tables and fields to lower case when using
+ * <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + oci8
+ *
+ *
+ * <samp>DB_PORTABILITY_RTRIM</samp>
+ * right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
+ *
+ *
+ * <samp>DB_PORTABILITY_DELETE_COUNT</samp>
+ * force reporting the number of rows deleted
+ *
+ * Some DBMS's don't count the number of rows deleted when performing
+ * simple <kbd>DELETE FROM tablename</kbd> queries. This portability
+ * mode tricks such DBMS's into telling the count by adding
+ * <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + fbsql
+ * + mysql
+ * + mysqli
+ * + sqlite
+ *
+ *
+ * <samp>DB_PORTABILITY_NUMROWS</samp>
+ * enable hack that makes <kbd>numRows()</kbd> work in Oracle
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + oci8
+ *
+ *
+ * <samp>DB_PORTABILITY_ERRORS</samp>
+ * makes certain error messages in certain drivers compatible
+ * with those from other DBMS's
+ *
+ * + mysql, mysqli: change unique/primary key constraints
+ * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
+ *
+ * + odbc(access): MS's ODBC driver reports 'no such field' as code
+ * 07001, which means 'too few parameters.' When this option is on
+ * that code gets mapped to DB_ERROR_NOSUCHFIELD.
+ * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
+ *
+ * <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
+ * convert null values to empty strings in data output by get*() and
+ * fetch*(). Needed because Oracle considers empty strings to be null,
+ * while most other DBMS's know the difference between empty and null.
+ *
+ *
+ * <samp>DB_PORTABILITY_ALL</samp>
+ * turn on all portability features
+ *
+ * -----------------------------------------
+ *
+ * Example 1. Simple setOption() example
+ * <code>
+ * $db->setOption('autofree', true);
+ * </code>
+ *
+ * Example 2. Portability for lowercasing and trimming
+ * <code>
+ * $db->setOption('portability',
+ * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
+ * </code>
+ *
+ * Example 3. All portability options except trimming
+ * <code>
+ * $db->setOption('portability',
+ * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
+ * </code>
+ *
+ * @param string $option option name
+ * @param mixed $value value for the option
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::$options
+ */
+ function setOption($option, $value)
+ {
+ if (isset($this->options[$option])) {
+ $this->options[$option] = $value;
+
+ /*
+ * Backwards compatibility check for the deprecated 'optimize'
+ * option. Done here in case settings change after connecting.
+ */
+ if ($option == 'optimize') {
+ if ($value == 'portability') {
+ switch ($this->phptype) {
+ case 'oci8':
+ $this->options['portability'] =
+ DB_PORTABILITY_LOWERCASE |
+ DB_PORTABILITY_NUMROWS;
+ break;
+ case 'fbsql':
+ case 'mysql':
+ case 'mysqli':
+ case 'sqlite':
+ $this->options['portability'] =
+ DB_PORTABILITY_DELETE_COUNT;
+ break;
+ }
+ } else {
+ $this->options['portability'] = DB_PORTABILITY_NONE;
+ }
+ }
+
+ return DB_OK;
+ }
+ return $this->raiseError("unknown option $option");
+ }
+
+ // }}}
+ // {{{ getOption()
+
+ /**
+ * Returns the value of an option
+ *
+ * @param string $option the option name you're curious about
+ *
+ * @return mixed the option's value
+ */
+ function getOption($option)
+ {
+ if (isset($this->options[$option])) {
+ return $this->options[$option];
+ }
+ return $this->raiseError("unknown option $option");
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute()
+ *
+ * Creates a query that can be run multiple times. Each time it is run,
+ * the placeholders, if any, will be replaced by the contents of
+ * execute()'s $data argument.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> scalar value (i.e. strings, integers). The system
+ * will automatically quote and escape the data.
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Example 1.
+ * <code>
+ * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
+ * $data = array(
+ * "John's text",
+ * "'it''s good'",
+ * 'filename.txt'
+ * );
+ * $res = $db->execute($sth, $data);
+ * </code>
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders:
+ * <pre>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </pre>
+ *
+ * With some database backends, this is emulated.
+ *
+ * {@internal ibase and oci8 have their own prepare() methods.}}
+ *
+ * @param string $query the query to be prepared
+ *
+ * @return mixed DB statement resource on success. A DB_Error object
+ * on failure.
+ *
+ * @see DB_common::execute()
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $token = 0;
+ $types = array();
+ $newtokens = array();
+
+ foreach ($tokens as $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ break;
+ default:
+ $newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ }
+ }
+
+ $this->prepare_tokens[] = &$newtokens;
+ end($this->prepare_tokens);
+
+ $k = key($this->prepare_tokens);
+ $this->prepare_types[$k] = $types;
+ $this->prepared_queries[$k] = implode(' ', $newtokens);
+
+ return $k;
+ }
+
+ // }}}
+ // {{{ autoPrepare()
+
+ /**
+ * Automaticaly generates an insert or update query and pass it to prepare()
+ *
+ * @param string $table the table name
+ * @param array $table_fields the array of field names
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return resource the query handle
+ *
+ * @uses DB_common::prepare(), DB_common::buildManipSQL()
+ */
+ function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT,
+ $where = false)
+ {
+ $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
+ if (DB::isError($query)) {
+ return $query;
+ }
+ return $this->prepare($query);
+ }
+
+ // }}}
+ // {{{ autoExecute()
+
+ /**
+ * Automaticaly generates an insert or update query and call prepare()
+ * and execute() with it
+ *
+ * @param string $table the table name
+ * @param array $fields_values the associative array where $key is a
+ * field name and $value its value
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * @uses DB_common::autoPrepare(), DB_common::execute()
+ */
+ function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT,
+ $where = false)
+ {
+ $sth = $this->autoPrepare($table, array_keys($fields_values), $mode,
+ $where);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $ret =& $this->execute($sth, array_values($fields_values));
+ $this->freePrepared($sth);
+ return $ret;
+
+ }
+
+ // }}}
+ // {{{ buildManipSQL()
+
+ /**
+ * Produces an SQL query string for autoPrepare()
+ *
+ * Example:
+ * <pre>
+ * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
+ * DB_AUTOQUERY_INSERT);
+ * </pre>
+ *
+ * That returns
+ * <samp>
+ * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
+ * </samp>
+ *
+ * NOTES:
+ * - This belongs more to a SQL Builder class, but this is a simple
+ * facility.
+ * - Be carefull! If you don't give a $where param with an UPDATE
+ * query, all the records of the table will be updated!
+ *
+ * @param string $table the table name
+ * @param array $table_fields the array of field names
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return string the sql query for autoPrepare()
+ */
+ function buildManipSQL($table, $table_fields, $mode, $where = false)
+ {
+ if (count($table_fields) == 0) {
+ return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+ $first = true;
+ switch ($mode) {
+ case DB_AUTOQUERY_INSERT:
+ $values = '';
+ $names = '';
+ foreach ($table_fields as $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $names .= ',';
+ $values .= ',';
+ }
+ $names .= $value;
+ $values .= '?';
+ }
+ return "INSERT INTO $table ($names) VALUES ($values)";
+ case DB_AUTOQUERY_UPDATE:
+ $set = '';
+ foreach ($table_fields as $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $set .= ',';
+ }
+ $set .= "$value = ?";
+ }
+ $sql = "UPDATE $table SET $set";
+ if ($where) {
+ $sql .= " WHERE $where";
+ }
+ return $sql;
+ default:
+ return $this->raiseError(DB_ERROR_SYNTAX);
+ }
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare()
+ *
+ * Example 1.
+ * <code>
+ * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
+ * $data = array(
+ * "John's text",
+ * "'it''s good'",
+ * 'filename.txt'
+ * );
+ * $res =& $db->execute($sth, $data);
+ * </code>
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * {@internal ibase and oci8 have their own execute() methods.}}
+ *
+ * @see DB_common::prepare()
+ */
+ function &execute($stmt, $data = array())
+ {
+ $realquery = $this->executeEmulateQuery($stmt, $data);
+ if (DB::isError($realquery)) {
+ return $realquery;
+ }
+ $result = $this->simpleQuery($realquery);
+
+ if ($result === DB_OK || DB::isError($result)) {
+ return $result;
+ } else {
+ $tmp =& new DB_result($this, $result);
+ return $tmp;
+ }
+ }
+
+ // }}}
+ // {{{ executeEmulateQuery()
+
+ /**
+ * Emulates executing prepared statements if the DBMS not support them
+ *
+ * @param resource $stmt a DB statement resource returned from execute()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a string containing the real query run when emulating
+ * prepare/execute. A DB_Error object on failure.
+ *
+ * @access protected
+ * @see DB_common::execute()
+ */
+ function executeEmulateQuery($stmt, $data = array())
+ {
+ $stmt = (int)$stmt;
+ $data = (array)$data;
+ $this->last_parameters = $data;
+
+ if (count($this->prepare_types[$stmt]) != count($data)) {
+ $this->last_query = $this->prepared_queries[$stmt];
+ return $this->raiseError(DB_ERROR_MISMATCH);
+ }
+
+ $realquery = $this->prepare_tokens[$stmt][0];
+
+ $i = 0;
+ foreach ($data as $value) {
+ if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
+ $realquery .= $this->quoteSmart($value);
+ } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($value, 'rb');
+ if (!$fp) {
+ return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+ $realquery .= $this->quoteSmart(fread($fp, filesize($value)));
+ fclose($fp);
+ } else {
+ $realquery .= $value;
+ }
+
+ $realquery .= $this->prepare_tokens[$stmt][++$i];
+ }
+
+ return $realquery;
+ }
+
+ // }}}
+ // {{{ executeMultiple()
+
+ /**
+ * Performs several execute() calls on the same statement handle
+ *
+ * $data must be an array indexed numerically
+ * from 0, one execute call is done for every "row" in the array.
+ *
+ * If an error occurs during execute(), executeMultiple() does not
+ * execute the unfinished rows, but rather returns that error.
+ *
+ * @param resource $stmt query handle from prepare()
+ * @param array $data numeric array containing the
+ * data to insert into the query
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::prepare(), DB_common::execute()
+ */
+ function executeMultiple($stmt, $data)
+ {
+ foreach ($data as $value) {
+ $res =& $this->execute($stmt, $value);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freePrepared()
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's PHP resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_common::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ $stmt = (int)$stmt;
+ if (isset($this->prepare_tokens[$stmt])) {
+ unset($this->prepare_tokens[$stmt]);
+ unset($this->prepare_types[$stmt]);
+ unset($this->prepared_queries[$stmt]);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * It is defined here to ensure all drivers have this method available.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(),
+ * DB_sqlite::modifyQuery()
+ */
+ function modifyQuery($query)
+ {
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * It is defined here to assure that all implementations
+ * have this method defined.
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return $query;
+ }
+
+ // }}}
+ // {{{ query()
+
+ /**
+ * Sends a query to the database server
+ *
+ * The query string can be either a normal statement to be sent directly
+ * to the server OR if <var>$params</var> are passed the query can have
+ * placeholders and it will be passed through prepare() and execute().
+ *
+ * @param string $query the SQL query or the statement to prepare
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * @see DB_result, DB_common::prepare(), DB_common::execute()
+ */
+ function &query($query, $params = array())
+ {
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $ret =& $this->execute($sth, $params);
+ $this->freePrepared($sth, false);
+ return $ret;
+ } else {
+ $this->last_parameters = array();
+ $result = $this->simpleQuery($query);
+ if ($result === DB_OK || DB::isError($result)) {
+ return $result;
+ } else {
+ $tmp =& new DB_result($this, $result);
+ return $tmp;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ limitQuery()
+
+ /**
+ * Generates and executes a LIMIT query
+ *
+ * @param string $query the query
+ * @param intr $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ */
+ function &limitQuery($query, $from, $count, $params = array())
+ {
+ $query = $this->modifyLimitQuery($query, $from, $count, $params);
+ if (DB::isError($query)){
+ return $query;
+ }
+ $result =& $this->query($query, $params);
+ if (is_a($result, 'DB_result')) {
+ $result->setOption('limit_from', $from);
+ $result->setOption('limit_count', $count);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Fetches the first column of the first row from a query result
+ *
+ * Takes care of doing the query and freeing the results when finished.
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed the returned value of the query.
+ * A DB_Error object on failure.
+ */
+ function &getOne($query, $params = array())
+ {
+ $params = (array)$params;
+ // modifyLimitQuery() would be nice here, but it causes BC issues
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
+ $res->free();
+
+ if ($err !== DB_OK) {
+ return $err;
+ }
+
+ return $row[0];
+ }
+
+ // }}}
+ // {{{ getRow()
+
+ /**
+ * Fetches the first row of data returned from a query result
+ *
+ * Takes care of doing the query and freeing the results when finished.
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use
+ *
+ * @return array the first row of results as an array.
+ * A DB_Error object on failure.
+ */
+ function &getRow($query, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT)
+ {
+ // compat check, the params and fetchmode parameters used to
+ // have the opposite order
+ if (!is_array($params)) {
+ if (is_array($fetchmode)) {
+ if ($params === null) {
+ $tmp = DB_FETCHMODE_DEFAULT;
+ } else {
+ $tmp = $params;
+ }
+ $params = $fetchmode;
+ $fetchmode = $tmp;
+ } elseif ($params !== null) {
+ $fetchmode = $params;
+ $params = array();
+ }
+ }
+ // modifyLimitQuery() would be nice here, but it causes BC issues
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $err = $res->fetchInto($row, $fetchmode);
+
+ $res->free();
+
+ if ($err !== DB_OK) {
+ return $err;
+ }
+
+ return $row;
+ }
+
+ // }}}
+ // {{{ getCol()
+
+ /**
+ * Fetches a single column from a query result and returns it as an
+ * indexed array
+ *
+ * @param string $query the SQL query
+ * @param mixed $col which column to return (integer [column number,
+ * starting at 0] or string [column name])
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return array the results as an array. A DB_Error object on failure.
+ *
+ * @see DB_common::query()
+ */
+ function &getCol($query, $col = 0, $params = array())
+ {
+ $params = (array)$params;
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
+
+ if (!is_array($row = $res->fetchRow($fetchmode))) {
+ $ret = array();
+ } else {
+ if (!array_key_exists($col, $row)) {
+ $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
+ } else {
+ $ret = array($row[$col]);
+ while (is_array($row = $res->fetchRow($fetchmode))) {
+ $ret[] = $row[$col];
+ }
+ }
+ }
+
+ $res->free();
+
+ if (DB::isError($row)) {
+ $ret = $row;
+ }
+
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getAssoc()
+
+ /**
+ * Fetches an entire query result and returns it as an
+ * associative array using the first column as the key
+ *
+ * If the result set contains more than two columns, the value
+ * will be an array of the values from column 2-n. If the result
+ * set contains only two columns, the returned value will be a
+ * scalar with the value of the second column (unless forced to an
+ * array with the $force_array parameter). A DB error code is
+ * returned on errors. If the result set contains fewer than two
+ * columns, a DB_ERROR_TRUNCATED error is returned.
+ *
+ * For example, if the table "mytable" contains:
+ *
+ * <pre>
+ * ID TEXT DATE
+ * --------------------------------
+ * 1 'one' 944679408
+ * 2 'two' 944679408
+ * 3 'three' 944679408
+ * </pre>
+ *
+ * Then the call getAssoc('SELECT id,text FROM mytable') returns:
+ * <pre>
+ * array(
+ * '1' => 'one',
+ * '2' => 'two',
+ * '3' => 'three',
+ * )
+ * </pre>
+ *
+ * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
+ * <pre>
+ * array(
+ * '1' => array('one', '944679408'),
+ * '2' => array('two', '944679408'),
+ * '3' => array('three', '944679408')
+ * )
+ * </pre>
+ *
+ * If the more than one row occurs with the same value in the
+ * first column, the last row overwrites all previous ones by
+ * default. Use the $group parameter if you don't want to
+ * overwrite like this. Example:
+ *
+ * <pre>
+ * getAssoc('SELECT category,id,name FROM mytable', false, null,
+ * DB_FETCHMODE_ASSOC, true) returns:
+ *
+ * array(
+ * '1' => array(array('id' => '4', 'name' => 'number four'),
+ * array('id' => '6', 'name' => 'number six')
+ * ),
+ * '9' => array(array('id' => '4', 'name' => 'number four'),
+ * array('id' => '6', 'name' => 'number six')
+ * )
+ * )
+ * </pre>
+ *
+ * Keep in mind that database functions in PHP usually return string
+ * values for results regardless of the database's internal type.
+ *
+ * @param string $query the SQL query
+ * @param bool $force_array used only when the query returns
+ * exactly two columns. If true, the values
+ * of the returned array will be one-element
+ * arrays instead of scalars.
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of
+ * items passed must match quantity of
+ * placeholders in query: meaning 1
+ * placeholder for non-array parameters or
+ * 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use
+ * @param bool $group if true, the values of the returned array
+ * is wrapped in another array. If the same
+ * key value (in the first column) repeats
+ * itself, the values will be appended to
+ * this array instead of overwriting the
+ * existing values.
+ *
+ * @return array the associative array containing the query results.
+ * A DB_Error object on failure.
+ */
+ function &getAssoc($query, $force_array = false, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
+ {
+ $params = (array)$params;
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+ if ($fetchmode == DB_FETCHMODE_DEFAULT) {
+ $fetchmode = $this->fetchmode;
+ }
+ $cols = $res->numCols();
+
+ if ($cols < 2) {
+ $tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
+ return $tmp;
+ }
+
+ $results = array();
+
+ if ($cols > 2 || $force_array) {
+ // return array values
+ // XXX this part can be optimized
+ if ($fetchmode == DB_FETCHMODE_ASSOC) {
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
+ reset($row);
+ $key = current($row);
+ unset($row[key($row)]);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ } elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
+ while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
+ $arr = get_object_vars($row);
+ $key = current($arr);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ } else {
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
+ // we shift away the first element to get
+ // indices running from 0 again
+ $key = array_shift($row);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ }
+ if (DB::isError($row)) {
+ $results = $row;
+ }
+ } else {
+ // return scalar values
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
+ if ($group) {
+ $results[$row[0]][] = $row[1];
+ } else {
+ $results[$row[0]] = $row[1];
+ }
+ }
+ if (DB::isError($row)) {
+ $results = $row;
+ }
+ }
+
+ $res->free();
+
+ return $results;
+ }
+
+ // }}}
+ // {{{ getAll()
+
+ /**
+ * Fetches all of the rows from a query result
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of
+ * items passed must match quantity of
+ * placeholders in query: meaning 1
+ * placeholder for non-array parameters or
+ * 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use:
+ * + DB_FETCHMODE_ORDERED
+ * + DB_FETCHMODE_ASSOC
+ * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
+ * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
+ *
+ * @return array the nested array. A DB_Error object on failure.
+ */
+ function &getAll($query, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT)
+ {
+ // compat check, the params and fetchmode parameters used to
+ // have the opposite order
+ if (!is_array($params)) {
+ if (is_array($fetchmode)) {
+ if ($params === null) {
+ $tmp = DB_FETCHMODE_DEFAULT;
+ } else {
+ $tmp = $params;
+ }
+ $params = $fetchmode;
+ $fetchmode = $tmp;
+ } elseif ($params !== null) {
+ $fetchmode = $params;
+ $params = array();
+ }
+ }
+
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if ($res === DB_OK || DB::isError($res)) {
+ return $res;
+ }
+
+ $results = array();
+ while (DB_OK === $res->fetchInto($row, $fetchmode)) {
+ if ($fetchmode & DB_FETCHMODE_FLIPPED) {
+ foreach ($row as $key => $val) {
+ $results[$key][] = $val;
+ }
+ } else {
+ $results[] = $row;
+ }
+ }
+
+ $res->free();
+
+ if (DB::isError($row)) {
+ $tmp =& $this->raiseError($row);
+ return $tmp;
+ }
+ return $results;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Determines the number of rows in a query result
+ *
+ * @param resource $result the query result idenifier produced by PHP
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function numRows($result)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ getSequenceName()
+
+ /**
+ * Generates the name used inside the database for a sequence
+ *
+ * The createSequence() docblock contains notes about storing sequence
+ * names.
+ *
+ * @param string $sqn the sequence's public name
+ *
+ * @return string the sequence's name in the backend
+ *
+ * @access protected
+ * @see DB_common::createSequence(), DB_common::dropSequence(),
+ * DB_common::nextID(), DB_common::setOption()
+ */
+ function getSequenceName($sqn)
+ {
+ return sprintf($this->getOption('seqname_format'),
+ preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::dropSequence(),
+ * DB_common::getSequenceName()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * The name of a given sequence is determined by passing the string
+ * provided in the <var>$seq_name</var> argument through PHP's sprintf()
+ * function using the value from the <var>seqname_format</var> option as
+ * the sprintf()'s format argument.
+ *
+ * <var>seqname_format</var> is set via setOption().
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_common::nextID()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_common::nextID()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * Communicates an error and invoke error callbacks, etc
+ *
+ * Basically a wrapper for PEAR::raiseError without the message string.
+ *
+ * @param mixed integer error code, or a PEAR error object (all
+ * other parameters are ignored if this parameter is
+ * an object
+ * @param int error mode, see PEAR_Error docs
+ * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the
+ * error level (E_USER_NOTICE etc). If error mode is
+ * PEAR_ERROR_CALLBACK, this is the callback function,
+ * either as a function name, or as an array of an
+ * object and method name. For other error modes this
+ * parameter is ignored.
+ * @param string extra debug information. Defaults to the last
+ * query and native error code.
+ * @param mixed native error code, integer or string depending the
+ * backend
+ *
+ * @return object the PEAR_Error object
+ *
+ * @see PEAR_Error
+ */
+ function &raiseError($code = DB_ERROR, $mode = null, $options = null,
+ $userinfo = null, $nativecode = null)
+ {
+ // The error is yet a DB error object
+ if (is_object($code)) {
+ // because we the static PEAR::raiseError, our global
+ // handler should be used if it is set
+ if ($mode === null && !empty($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ }
+ $tmp = PEAR::raiseError($code, null, $mode, $options,
+ null, null, true);
+ return $tmp;
+ }
+
+ if ($userinfo === null) {
+ $userinfo = $this->last_query;
+ }
+
+ if ($nativecode) {
+ $userinfo .= ' [nativecode=' . trim($nativecode) . ']';
+ } else {
+ $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']';
+ }
+
+ $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
+ 'DB_Error', true);
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return mixed the DBMS' error code. A DB_Error object on failure.
+ */
+ function errorNative()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones
+ *
+ * Uses the <var>$errorcode_map</var> property defined in each driver.
+ *
+ * @param string|int $nativecode the error code returned by the DBMS
+ *
+ * @return int the portable DB error code. Return DB_ERROR if the
+ * current driver doesn't have a mapping for the
+ * $nativecode submitted.
+ */
+ function errorCode($nativecode)
+ {
+ if (isset($this->errorcode_map[$nativecode])) {
+ return $this->errorcode_map[$nativecode];
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ errorMessage()
+
+ /**
+ * Maps a DB error code to a textual message
+ *
+ * @param integer $dbcode the DB error code
+ *
+ * @return string the error message corresponding to the error code
+ * submitted. FALSE if the error code is unknown.
+ *
+ * @see DB::errorMessage()
+ */
+ function errorMessage($dbcode)
+ {
+ return DB::errorMessage($this->errorcode_map[$dbcode]);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * The format of the resulting array depends on which <var>$mode</var>
+ * you select. The sample output below is based on this query:
+ * <pre>
+ * SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
+ * FROM tblFoo
+ * JOIN tblBar ON tblFoo.fldId = tblBar.fldId
+ * </pre>
+ *
+ * <ul>
+ * <li>
+ *
+ * <kbd>null</kbd> (default)
+ * <pre>
+ * [0] => Array (
+ * [table] => tblFoo
+ * [name] => fldId
+ * [type] => int
+ * [len] => 11
+ * [flags] => primary_key not_null
+ * )
+ * [1] => Array (
+ * [table] => tblFoo
+ * [name] => fldPhone
+ * [type] => string
+ * [len] => 20
+ * [flags] =>
+ * )
+ * [2] => Array (
+ * [table] => tblBar
+ * [name] => fldId
+ * [type] => int
+ * [len] => 11
+ * [flags] => primary_key not_null
+ * )
+ * </pre>
+ *
+ * </li><li>
+ *
+ * <kbd>DB_TABLEINFO_ORDER</kbd>
+ *
+ * <p>In addition to the information found in the default output,
+ * a notation of the number of columns is provided by the
+ * <samp>num_fields</samp> element while the <samp>order</samp>
+ * element provides an array with the column names as the keys and
+ * their location index number (corresponding to the keys in the
+ * the default output) as the values.</p>
+ *
+ * <p>If a result set has identical field names, the last one is
+ * used.</p>
+ *
+ * <pre>
+ * [num_fields] => 3
+ * [order] => Array (
+ * [fldId] => 2
+ * [fldTrans] => 1
+ * )
+ * </pre>
+ *
+ * </li><li>
+ *
+ * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
+ *
+ * <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
+ * dimensions to the array in which the table names are keys and
+ * the field names are sub-keys. This is helpful for queries that
+ * join tables which have identical field names.</p>
+ *
+ * <pre>
+ * [num_fields] => 3
+ * [ordertable] => Array (
+ * [tblFoo] => Array (
+ * [fldId] => 0
+ * [fldPhone] => 1
+ * )
+ * [tblBar] => Array (
+ * [fldId] => 2
+ * )
+ * )
+ * </pre>
+ *
+ * </li>
+ * </ul>
+ *
+ * The <samp>flags</samp> element contains a space separated list
+ * of extra information about the field. This data is inconsistent
+ * between DBMS's due to the way each DBMS works.
+ * + <samp>primary_key</samp>
+ * + <samp>unique_key</samp>
+ * + <samp>multiple_key</samp>
+ * + <samp>not_null</samp>
+ *
+ * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
+ * elements if <var>$result</var> is a table name. The following DBMS's
+ * provide full information from queries:
+ * + fbsql
+ * + mysql
+ *
+ * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
+ * turned on, the names of tables and fields will be lowercased.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode either unused or one of the tableInfo modes:
+ * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
+ * <kbd>DB_TABLEINFO_ORDER</kbd> or
+ * <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
+ * These are bitwise, so the first two can be
+ * combined using <kbd>|</kbd>.
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ /*
+ * If the DB_<driver> class has a tableInfo() method, that one
+ * overrides this one. But, if the driver doesn't have one,
+ * this method runs and tells users about that fact.
+ */
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ getTables()
+
+ /**
+ * Lists the tables in the current database
+ *
+ * @return array the list of tables. A DB_Error object on failure.
+ *
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function getTables()
+ {
+ return $this->getListOf('tables');
+ }
+
+ // }}}
+ // {{{ getListOf()
+
+ /**
+ * Lists internal database information
+ *
+ * @param string $type type of information being sought.
+ * Common items being sought are:
+ * tables, databases, users, views, functions
+ * Each DBMS's has its own capabilities.
+ *
+ * @return array an array listing the items sought.
+ * A DB DB_Error object on failure.
+ */
+ function getListOf($type)
+ {
+ $sql = $this->getSpecialQuery($type);
+ if ($sql === null) {
+ $this->last_query = '';
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ } elseif (is_int($sql) || DB::isError($sql)) {
+ // Previous error
+ return $this->raiseError($sql);
+ } elseif (is_array($sql)) {
+ // Already the result
+ return $sql;
+ }
+ // Launch this query
+ return $this->getCol($sql);
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ }
+
+ // }}}
+ // {{{ _rtrimArrayValues()
+
+ /**
+ * Right-trims all strings in an array
+ *
+ * @param array $array the array to be trimmed (passed by reference)
+ *
+ * @return void
+ *
+ * @access protected
+ */
+ function _rtrimArrayValues(&$array)
+ {
+ foreach ($array as $key => $value) {
+ if (is_string($value)) {
+ $array[$key] = rtrim($value);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _convertNullArrayValuesToEmpty()
+
+ /**
+ * Converts all null values in an array to empty strings
+ *
+ * @param array $array the array to be de-nullified (passed by reference)
+ *
+ * @return void
+ *
+ * @access protected
+ */
+ function _convertNullArrayValuesToEmpty(&$array)
+ {
+ foreach ($array as $key => $value) {
+ if (is_null($value)) {
+ $array[$key] = '';
+ }
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's dbase extension
+ * for interacting with dBase databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: dbase.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's dbase extension
+ * for interacting with dBase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_dbase extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'dbase';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'dbase';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => false,
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => false,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * A means of emulating result resources
+ * @var array
+ */
+ var $res_row = array();
+
+ /**
+ * The quantity of results so far
+ *
+ * For emulating result resources.
+ *
+ * @var integer
+ */
+ var $result = 0;
+
+ /**
+ * Maps dbase data type id's to human readable strings
+ *
+ * The human readable values are based on the output of PHP's
+ * dbase_get_header_info() function.
+ *
+ * @var array
+ * @since Property available since Release 1.7.0
+ */
+ var $types = array(
+ 'C' => 'character',
+ 'D' => 'date',
+ 'L' => 'boolean',
+ 'M' => 'memo',
+ 'N' => 'number',
+ );
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_dbase()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database and create it if it doesn't exist
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's dbase driver supports the following extra DSN options:
+ * + mode An integer specifying the read/write mode to use
+ * (0 = read only, 1 = write only, 2 = read/write).
+ * Available since PEAR DB 1.7.0.
+ * + fields An array of arrays that PHP's dbase_create() function needs
+ * to create a new database. This information is used if the
+ * dBase file specified in the "database" segment of the DSN
+ * does not exist. For more info, see the PHP manual's
+ * {@link http://php.net/dbase_create dbase_create()} page.
+ * Available since PEAR DB 1.7.0.
+ *
+ * Example of how to connect and establish a new dBase file if necessary:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = array(
+ * 'phptype' => 'dbase',
+ * 'database' => '/path/and/name/of/dbase/file',
+ * 'mode' => 2,
+ * 'fields' => array(
+ * array('a', 'N', 5, 0),
+ * array('b', 'C', 40),
+ * array('c', 'C', 255),
+ * array('d', 'C', 20),
+ * ),
+ * );
+ * $options = array(
+ * 'debug' => 2,
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('dbase')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ /*
+ * Turn track_errors on for entire script since $php_errormsg
+ * is the only way to find errors from the dbase extension.
+ */
+ ini_set('track_errors', 1);
+ $php_errormsg = '';
+
+ if (!file_exists($dsn['database'])) {
+ $this->dsn['mode'] = 2;
+ if (empty($dsn['fields']) || !is_array($dsn['fields'])) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ 'the dbase file does not exist and '
+ . 'it could not be created because '
+ . 'the "fields" element of the DSN '
+ . 'is not properly set');
+ }
+ $this->connection = @dbase_create($dsn['database'],
+ $dsn['fields']);
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ 'the dbase file does not exist and '
+ . 'the attempt to create it failed: '
+ . $php_errormsg);
+ }
+ } else {
+ if (!isset($this->dsn['mode'])) {
+ $this->dsn['mode'] = 0;
+ }
+ $this->connection = @dbase_open($dsn['database'],
+ $this->dsn['mode']);
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @dbase_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ &query()
+
+ function &query($query = null)
+ {
+ // emulate result resources
+ $this->res_row[(int)$this->result] = 0;
+ $tmp =& new DB_result($this, $this->result++);
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum === null) {
+ $rownum = $this->res_row[(int)$result]++;
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @dbase_get_record_with_names($this->connection, $rownum);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @dbase_get_record($this->connection, $rownum);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($foo)
+ {
+ return @dbase_numfields($this->connection);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($foo)
+ {
+ return @dbase_numrecords($this->connection);
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * + null = the string <samp>NULL</samp>
+ * + boolean = <samp>T</samp> if true or
+ * <samp>F</samp> if false. Use the <kbd>Logical</kbd>
+ * data type.
+ * + integer or double = the unquoted number
+ * + other (including strings and numeric strings) =
+ * the data with single quotes escaped by preceeding
+ * single quotes then the whole string is encapsulated
+ * between single quotes
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in) || is_double($in)) {
+ return $in;
+ } elseif (is_bool($in)) {
+ return $in ? 'T' : 'F';
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about the current database
+ *
+ * @param mixed $result THIS IS UNUSED IN DBASE. The current database
+ * is examined regardless of what is provided here.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result = null, $mode = null)
+ {
+ if (function_exists('dbase_get_header_info')) {
+ $id = @dbase_get_header_info($this->connection);
+ if (!$id && $php_errormsg) {
+ return $this->raiseError(DB_ERROR,
+ null, null, null,
+ $php_errormsg);
+ }
+ } else {
+ /*
+ * This segment for PHP 4 is loosely based on code by
+ * Hadi Rusiah <deegos@yahoo.com> in the comments on
+ * the dBase reference page in the PHP manual.
+ */
+ $db = @fopen($this->dsn['database'], 'r');
+ if (!$db) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+
+ $id = array();
+ $i = 0;
+
+ $line = fread($db, 32);
+ while (!feof($db)) {
+ $line = fread($db, 32);
+ if (substr($line, 0, 1) == chr(13)) {
+ break;
+ } else {
+ $pos = strpos(substr($line, 0, 10), chr(0));
+ $pos = ($pos == 0 ? 10 : $pos);
+ $id[$i] = array(
+ 'name' => substr($line, 0, $pos),
+ 'type' => $this->types[substr($line, 11, 1)],
+ 'length' => ord(substr($line, 16, 1)),
+ 'precision' => ord(substr($line, 17, 1)),
+ );
+ }
+ $i++;
+ }
+
+ fclose($db);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $res = array();
+ $count = count($id);
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $this->dsn['database'],
+ 'name' => $case_func($id[$i]['name']),
+ 'type' => $id[$i]['type'],
+ 'len' => $id[$i]['length'],
+ 'flags' => ''
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ return $res;
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's fbsql extension
+ * for interacting with FrontBase databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Frank M. Kromann <frank@frontbase.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: fbsql.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's fbsql extension
+ * for interacting with FrontBase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Frank M. Kromann <frank@frontbase.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ * @since Class functional since Release 1.7.0
+ */
+class DB_fbsql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'fbsql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'fbsql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 22 => DB_ERROR_SYNTAX,
+ 85 => DB_ERROR_ALREADY_EXISTS,
+ 108 => DB_ERROR_SYNTAX,
+ 116 => DB_ERROR_NOSUCHTABLE,
+ 124 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 215 => DB_ERROR_NOSUCHFIELD,
+ 217 => DB_ERROR_INVALID_NUMBER,
+ 226 => DB_ERROR_NOSUCHFIELD,
+ 231 => DB_ERROR_INVALID,
+ 239 => DB_ERROR_TRUNCATED,
+ 251 => DB_ERROR_SYNTAX,
+ 266 => DB_ERROR_NOT_FOUND,
+ 357 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 358 => DB_ERROR_CONSTRAINT,
+ 360 => DB_ERROR_CONSTRAINT,
+ 361 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_fbsql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('fbsql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array(
+ $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
+ $dsn['username'] ? $dsn['username'] : null,
+ $dsn['password'] ? $dsn['password'] : null,
+ );
+
+ $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+
+ if ($dsn['database']) {
+ if (!@fbsql_select_db($dsn['database'], $this->connection)) {
+ return $this->fbsqlRaiseError();
+ }
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @fbsql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @fbsql_query("$query;", $this->connection);
+ if (!$result) {
+ return $this->fbsqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if (DB::isManip($query)) {
+ return DB_OK;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal fbsql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return @fbsql_next_result($result);
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@fbsql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @fbsql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @fbsql_free_result($result);
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff=false)
+ {
+ if ($onoff) {
+ $this->query("SET COMMIT TRUE");
+ } else {
+ $this->query("SET COMMIT FALSE");
+ }
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ @fbsql_commit();
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ @fbsql_rollback();
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @fbsql_num_fields($result);
+ if (!$cols) {
+ return $this->fbsqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @fbsql_num_rows($result);
+ if ($rows === null) {
+ return $this->fbsqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ $result = @fbsql_affected_rows($this->connection);
+ } else {
+ $result = 0;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_fbsql::createSequence(), DB_fbsql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query('SELECT UNIQUE FROM ' . $seqname);
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $result;
+ }
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->fbsqlRaiseError();
+ }
+ $result->fetchInto($tmp, DB_FETCHMODE_ORDERED);
+ return $tmp[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_fbsql::nextID(), DB_fbsql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if ($res) {
+ $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_fbsql::nextID(), DB_fbsql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
+ . ' RESTRICT');
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query)) {
+ return preg_replace('/^([\s(])*SELECT/i',
+ "\\1SELECT TOP($count)", $query);
+ } else {
+ return preg_replace('/([\s(])*SELECT/i',
+ "\\1SELECT TOP($from, $count)", $query);
+ }
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * + null = the string <samp>NULL</samp>
+ * + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
+ * + integer or double = the unquoted number
+ * + other (including strings and numeric strings) =
+ * the data escaped according to FrontBase's settings
+ * then encapsulated between single quotes
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in) || is_double($in)) {
+ return $in;
+ } elseif (is_bool($in)) {
+ return $in ? 'TRUE' : 'FALSE';
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ fbsqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_fbsql::errorNative(), DB_common::errorCode()
+ */
+ function fbsqlRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $errno = $this->errorCode(fbsql_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @fbsql_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @fbsql_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @fbsql_list_fields($this->dsn['database'],
+ $result, $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @fbsql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $case_func(@fbsql_field_table($id, $i)),
+ 'name' => $case_func(@fbsql_field_name($id, $i)),
+ 'type' => @fbsql_field_type($id, $i),
+ 'len' => @fbsql_field_len($id, $i),
+ 'flags' => @fbsql_field_flags($id, $i),
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @fbsql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT "table_name" FROM information_schema.tables'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk AND'
+ . ' "table_type" = \'BASE TABLE\''
+ . ' AND "schema_name" = current_schema';
+ case 'views':
+ return 'SELECT "table_name" FROM information_schema.tables'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk AND'
+ . ' "table_type" = \'VIEW\''
+ . ' AND "schema_name" = current_schema';
+ case 'users':
+ return 'SELECT "user_name" from information_schema.users';
+ case 'functions':
+ return 'SELECT "routine_name" FROM'
+ . ' information_schema.psm_routines'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk'
+ . ' AND "routine_kind"=\'FUNCTION\''
+ . ' AND "schema_name" = current_schema';
+ case 'procedures':
+ return 'SELECT "routine_name" FROM'
+ . ' information_schema.psm_routines'
+ . ' t0, information_schema.schemata t1'
+ . ' WHERE t0.schema_pk=t1.schema_pk'
+ . ' AND "routine_kind"=\'PROCEDURE\''
+ . ' AND "schema_name" = current_schema';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's interbase extension
+ * for interacting with Interbase and Firebird databases
+ *
+ * While this class works with PHP 4, PHP's InterBase extension is
+ * unstable in PHP 4. Use PHP 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: ibase.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's interbase extension
+ * for interacting with Interbase and Firebird databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * While this class works with PHP 4, PHP's InterBase extension is
+ * unstable in PHP 4. Use PHP 5.
+ *
+ * NOTICE: limitQuery() only works for Firebird.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ * @since Class became stable in Release 1.7.0
+ */
+class DB_ibase extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'ibase';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'ibase';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * NOTE: only firebird supports limit.
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => false,
+ 'new_link' => false,
+ 'numrows' => 'emulate',
+ 'pconnect' => true,
+ 'prepare' => true,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ -104 => DB_ERROR_SYNTAX,
+ -150 => DB_ERROR_ACCESS_VIOLATION,
+ -151 => DB_ERROR_ACCESS_VIOLATION,
+ -155 => DB_ERROR_NOSUCHTABLE,
+ -157 => DB_ERROR_NOSUCHFIELD,
+ -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ -170 => DB_ERROR_MISMATCH,
+ -171 => DB_ERROR_MISMATCH,
+ -172 => DB_ERROR_INVALID,
+ // -204 => // Covers too many errors, need to use regex on msg
+ -205 => DB_ERROR_NOSUCHFIELD,
+ -206 => DB_ERROR_NOSUCHFIELD,
+ -208 => DB_ERROR_INVALID,
+ -219 => DB_ERROR_NOSUCHTABLE,
+ -297 => DB_ERROR_CONSTRAINT,
+ -303 => DB_ERROR_INVALID,
+ -413 => DB_ERROR_INVALID_NUMBER,
+ -530 => DB_ERROR_CONSTRAINT,
+ -551 => DB_ERROR_ACCESS_VIOLATION,
+ -552 => DB_ERROR_ACCESS_VIOLATION,
+ // -607 => // Covers too many errors, need to use regex on msg
+ -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ -803 => DB_ERROR_CONSTRAINT,
+ -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ -904 => DB_ERROR_CONNECT_FAILED,
+ -922 => DB_ERROR_NOSUCHDB,
+ -923 => DB_ERROR_CONNECT_FAILED,
+ -924 => DB_ERROR_CONNECT_FAILED
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ * @access private
+ */
+ var $affected = 0;
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The prepared statement handle from the most recently executed statement
+ *
+ * {@internal Mainly here because the InterBase/Firebird API is only
+ * able to retrieve data from result sets if the statemnt handle is
+ * still in scope.}}
+ *
+ * @var resource
+ */
+ var $last_stmt;
+
+ /**
+ * Is the given prepared statement a data manipulation query?
+ * @var array
+ * @access private
+ */
+ var $manip_query = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_ibase()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's ibase driver supports the following extra DSN options:
+ * + buffers The number of database buffers to allocate for the
+ * server-side cache.
+ * + charset The default character set for a database.
+ * + dialect The default SQL dialect for any statement
+ * executed within a connection. Defaults to the
+ * highest one supported by client libraries.
+ * Functional only with InterBase 6 and up.
+ * + role Functional only with InterBase 5 and up.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('interbase')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+ if ($this->dbsyntax == 'firebird') {
+ $this->features['limit'] = 'alter';
+ }
+
+ $params = array(
+ $dsn['hostspec']
+ ? ($dsn['hostspec'] . ':' . $dsn['database'])
+ : $dsn['database'],
+ $dsn['username'] ? $dsn['username'] : null,
+ $dsn['password'] ? $dsn['password'] : null,
+ isset($dsn['charset']) ? $dsn['charset'] : null,
+ isset($dsn['buffers']) ? $dsn['buffers'] : null,
+ isset($dsn['dialect']) ? $dsn['dialect'] : null,
+ isset($dsn['role']) ? $dsn['role'] : null,
+ );
+
+ $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
+
+ $this->connection = @call_user_func_array($connect_function, $params);
+ if (!$this->connection) {
+ return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @ibase_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @ibase_query($this->connection, $query);
+
+ if (!$result) {
+ return $this->ibaseRaiseError();
+ }
+ if ($this->autocommit && $ismanip) {
+ @ibase_commit($this->connection);
+ }
+ if ($ismanip) {
+ $this->affected = $result;
+ return DB_OK;
+ } else {
+ $this->affected = 0;
+ return $result;
+ }
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * Only works with Firebird.
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if ($this->dsn['dbsyntax'] == 'firebird') {
+ $query = preg_replace('/^([\s(])*SELECT/i',
+ "SELECT FIRST $count SKIP $from", $query);
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal ibase result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ if (function_exists('ibase_fetch_assoc')) {
+ $arr = @ibase_fetch_assoc($result);
+ } else {
+ $arr = get_object_vars(ibase_fetch_object($result));
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @ibase_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @ibase_free_result($result);
+ }
+
+ // }}}
+ // {{{ freeQuery()
+
+ function freeQuery($query)
+ {
+ @ibase_free_query($query);
+ return true;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (is_integer($this->affected)) {
+ return $this->affected;
+ }
+ return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @ibase_num_fields($result);
+ if (!$cols) {
+ return $this->ibaseRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute().
+ *
+ * prepare() requires a generic query as string like <code>
+ * INSERT INTO numbers VALUES (?, ?, ?)
+ * </code>. The <kbd>?</kbd> characters are placeholders.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders. Example: <code>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </code>
+ *
+ * @param string $query query to be prepared
+ * @return mixed DB statement resource on success. DB_Error on failure.
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $token = 0;
+ $types = array();
+ $newquery = '';
+
+ foreach ($tokens as $key => $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ break;
+ default:
+ $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ $newquery .= $tokens[$key] . '?';
+ }
+ }
+
+ $newquery = substr($newquery, 0, -1);
+ $this->last_query = $query;
+ $newquery = $this->modifyQuery($newquery);
+ $stmt = @ibase_prepare($this->connection, $newquery);
+ $this->prepare_types[(int)$stmt] = $types;
+ $this->manip_query[(int)$stmt] = DB::isManip($query);
+ return $stmt;
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare().
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 for non-array items or the
+ * quantity of elements in the array.
+ * @return object a new DB_Result or a DB_Error when fail
+ * @see DB_ibase::prepare()
+ * @access public
+ */
+ function &execute($stmt, $data = array())
+ {
+ $data = (array)$data;
+ $this->last_parameters = $data;
+
+ $types =& $this->prepare_types[(int)$stmt];
+ if (count($types) != count($data)) {
+ $tmp =& $this->raiseError(DB_ERROR_MISMATCH);
+ return $tmp;
+ }
+
+ $i = 0;
+ foreach ($data as $key => $value) {
+ if ($types[$i] == DB_PARAM_MISC) {
+ /*
+ * ibase doesn't seem to have the ability to pass a
+ * parameter along unchanged, so strip off quotes from start
+ * and end, plus turn two single quotes to one single quote,
+ * in order to avoid the quotes getting escaped by
+ * ibase and ending up in the database.
+ */
+ $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
+ $data[$key] = str_replace("''", "'", $data[$key]);
+ } elseif ($types[$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($data[$key], 'rb');
+ if (!$fp) {
+ $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ return $tmp;
+ }
+ $data[$key] = fread($fp, filesize($data[$key]));
+ fclose($fp);
+ }
+ $i++;
+ }
+
+ array_unshift($data, $stmt);
+
+ $res = call_user_func_array('ibase_execute', $data);
+ if (!$res) {
+ $tmp =& $this->ibaseRaiseError();
+ return $tmp;
+ }
+ /* XXX need this?
+ if ($this->autocommit && $this->manip_query[(int)$stmt]) {
+ @ibase_commit($this->connection);
+ }*/
+ $this->last_stmt = $stmt;
+ if ($this->manip_query[(int)$stmt]) {
+ $tmp = DB_OK;
+ } else {
+ $tmp =& new DB_result($this, $res);
+ }
+ return $tmp;
+ }
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's PHP resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_ibase::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ if (!is_resource($stmt)) {
+ return false;
+ }
+ if ($free_resource) {
+ @ibase_free_query($stmt);
+ }
+ unset($this->prepare_tokens[(int)$stmt]);
+ unset($this->prepare_types[(int)$stmt]);
+ unset($this->manip_query[(int)$stmt]);
+ return true;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ $this->autocommit = $onoff ? 1 : 0;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ return @ibase_commit($this->connection);
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ return @ibase_rollback($this->connection);
+ }
+
+ // }}}
+ // {{{ transactionInit()
+
+ function transactionInit($trans_args = 0)
+ {
+ return $trans_args
+ ? @ibase_trans($trans_args, $this->connection)
+ : @ibase_trans();
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_ibase::createSequence(), DB_ibase::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $sqn = strtoupper($this->getSequenceName($seq_name));
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result =& $this->query("SELECT GEN_ID(${sqn}, 1) "
+ . 'FROM RDB$GENERATORS '
+ . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result)) {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $result;
+ }
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_ibase::nextID(), DB_ibase::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $sqn = strtoupper($this->getSequenceName($seq_name));
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("CREATE GENERATOR ${sqn}");
+ $this->popErrorHandling();
+
+ return $result;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_ibase::nextID(), DB_ibase::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DELETE FROM RDB$GENERATORS '
+ . "WHERE RDB\$GENERATOR_NAME='"
+ . strtoupper($this->getSequenceName($seq_name))
+ . "'");
+ }
+
+ // }}}
+ // {{{ _ibaseFieldFlags()
+
+ /**
+ * Get the column's flags
+ *
+ * Supports "primary_key", "unique_key", "not_null", "default",
+ * "computed" and "blob".
+ *
+ * @param string $field_name the name of the field
+ * @param string $table_name the name of the table
+ *
+ * @return string the flags
+ *
+ * @access private
+ */
+ function _ibaseFieldFlags($field_name, $table_name)
+ {
+ $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
+ .' FROM RDB$INDEX_SEGMENTS I'
+ .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
+ .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
+ .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
+
+ $result = @ibase_query($this->connection, $sql);
+ if (!$result) {
+ return $this->ibaseRaiseError();
+ }
+
+ $flags = '';
+ if ($obj = @ibase_fetch_object($result)) {
+ @ibase_free_result($result);
+ if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') {
+ $flags .= 'primary_key ';
+ }
+ if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') {
+ $flags .= 'unique_key ';
+ }
+ }
+
+ $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
+ .' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
+ .' F.RDB$FIELD_TYPE AS FTYPE,'
+ .' F.RDB$COMPUTED_SOURCE AS CSOURCE'
+ .' FROM RDB$RELATION_FIELDS R '
+ .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
+ .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
+ .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
+
+ $result = @ibase_query($this->connection, $sql);
+ if (!$result) {
+ return $this->ibaseRaiseError();
+ }
+ if ($obj = @ibase_fetch_object($result)) {
+ @ibase_free_result($result);
+ if (isset($obj->NFLAG)) {
+ $flags .= 'not_null ';
+ }
+ if (isset($obj->DSOURCE)) {
+ $flags .= 'default ';
+ }
+ if (isset($obj->CSOURCE)) {
+ $flags .= 'computed ';
+ }
+ if (isset($obj->FTYPE) && $obj->FTYPE == 261) {
+ $flags .= 'blob ';
+ }
+ }
+
+ return trim($flags);
+ }
+
+ // }}}
+ // {{{ ibaseRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_ibase::errorNative(), DB_ibase::errorCode()
+ */
+ function &ibaseRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $errno = $this->errorCode($this->errorNative());
+ }
+ $tmp =& $this->raiseError($errno, null, null, null, @ibase_errmsg());
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code. NULL if there is no error code.
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function errorNative()
+ {
+ if (function_exists('ibase_errcode')) {
+ return @ibase_errcode();
+ }
+ if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
+ @ibase_errmsg(), $m)) {
+ return (int)$m[1];
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones
+ *
+ * @param int $nativecode the error code returned by the DBMS
+ *
+ * @return int the portable DB error code. Return DB_ERROR if the
+ * current driver doesn't have a mapping for the
+ * $nativecode submitted.
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function errorCode($nativecode = null)
+ {
+ if (isset($this->errorcode_map[$nativecode])) {
+ return $this->errorcode_map[$nativecode];
+ }
+
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/generator .* is not defined/'
+ => DB_ERROR_SYNTAX, // for compat. w ibase_errcode()
+ '/table.*(not exist|not found|unknown)/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/table .* already exists/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/unsuccessful metadata update .* not found/i'
+ => DB_ERROR_NOT_FOUND,
+ '/validation error for column .* value "\*\*\* null/i'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/violation of [\w ]+ constraint/i'
+ => DB_ERROR_CONSTRAINT,
+ '/conversion error from string/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/no permission for/i'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/arithmetic exception, numeric overflow, or string truncation/i'
+ => DB_ERROR_INVALID,
+ );
+ }
+
+ $errormsg = @ibase_errmsg();
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @ibase_query($this->connection,
+ "SELECT * FROM $result WHERE 1=0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @ibase_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $info = @ibase_field_info($id, $i);
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func($info['name']),
+ 'type' => $info['type'],
+ 'len' => $info['length'],
+ 'flags' => ($got_string)
+ ? $this->_ibaseFieldFlags($info['name'], $result)
+ : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @ibase_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
+ . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
+ case 'views':
+ return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
+ case 'users':
+ return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's ifx extension
+ * for interacting with Informix databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: ifx.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's ifx extension
+ * for interacting with Informix databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * More info on Informix errors can be found at:
+ * http://www.informix.com/answers/english/ierrors.htm
+ *
+ * TODO:
+ * - set needed env Informix vars on connect
+ * - implement native prepare/execute
+ *
+ * @category Database
+ * @package DB
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_ifx extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'ifx';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'ifx';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => 'emulate',
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ '-201' => DB_ERROR_SYNTAX,
+ '-206' => DB_ERROR_NOSUCHTABLE,
+ '-217' => DB_ERROR_NOSUCHFIELD,
+ '-236' => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '-239' => DB_ERROR_CONSTRAINT,
+ '-253' => DB_ERROR_SYNTAX,
+ '-292' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-310' => DB_ERROR_ALREADY_EXISTS,
+ '-316' => DB_ERROR_ALREADY_EXISTS,
+ '-319' => DB_ERROR_NOT_FOUND,
+ '-329' => DB_ERROR_NODBSELECTED,
+ '-346' => DB_ERROR_CONSTRAINT,
+ '-386' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-391' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-554' => DB_ERROR_SYNTAX,
+ '-691' => DB_ERROR_CONSTRAINT,
+ '-692' => DB_ERROR_CONSTRAINT,
+ '-703' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '-1204' => DB_ERROR_INVALID_DATE,
+ '-1205' => DB_ERROR_INVALID_DATE,
+ '-1206' => DB_ERROR_INVALID_DATE,
+ '-1209' => DB_ERROR_INVALID_DATE,
+ '-1210' => DB_ERROR_INVALID_DATE,
+ '-1212' => DB_ERROR_INVALID_DATE,
+ '-1213' => DB_ERROR_INVALID_NUMBER,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ * @access private
+ */
+ var $affected = 0;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_ifx()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('informix') &&
+ !PEAR::loadExtension('Informix'))
+ {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
+ $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
+ $user = $dsn['username'] ? $dsn['username'] : '';
+ $pw = $dsn['password'] ? $dsn['password'] : '';
+
+ $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
+
+ $this->connection = @$connect_function($dbname, $user, $pw);
+ if (!is_resource($this->connection)) {
+ return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @ifx_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $this->affected = null;
+ if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()?
+ // the scroll is needed for fetching absolute row numbers
+ // in a select query result
+ $result = @ifx_query($query, $this->connection, IFX_SCROLL);
+ } else {
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @ifx_query('BEGIN WORK', $this->connection);
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @ifx_query($query, $this->connection);
+ }
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ $this->affected = @ifx_affected_rows($result);
+ // Determine which queries should return data, and which
+ // should return an error code only.
+ if (preg_match('/(SELECT)/i', $query)) {
+ return $result;
+ }
+ // XXX Testme: free results inside a transaction
+ // may cause to stop it and commit the work?
+
+ // Result has to be freed even with a insert or update
+ @ifx_free_result($result);
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal ifx result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ return $this->affected;
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if (($rownum !== null) && ($rownum < 0)) {
+ return null;
+ }
+ if ($rownum === null) {
+ /*
+ * Even though fetch_row() should return the next row if
+ * $rownum is null, it doesn't in all cases. Bug 598.
+ */
+ $rownum = 'NEXT';
+ } else {
+ // Index starts at row 1, unlike most DBMS's starting at 0.
+ $rownum++;
+ }
+ if (!$arr = @ifx_fetch_row($result, $rownum)) {
+ return null;
+ }
+ if ($fetchmode !== DB_FETCHMODE_ASSOC) {
+ $i=0;
+ $order = array();
+ foreach ($arr as $val) {
+ $order[$i++] = $val;
+ }
+ $arr = $order;
+ } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
+ $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
+ {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ if (!$cols = @ifx_num_fields($result)) {
+ return $this->ifxRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @ifx_free_result($result);
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = true)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @ifx_query('COMMIT WORK', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @ifx_query('ROLLBACK WORK', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->ifxRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ ifxRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_ifx::errorNative(), DB_ifx::errorCode()
+ */
+ function ifxRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $errno = $this->errorCode(ifx_error());
+ }
+ return $this->raiseError($errno, null, null, null,
+ $this->errorNative());
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code and message produced by the last query
+ *
+ * @return string the DBMS' error code and message
+ */
+ function errorNative()
+ {
+ return @ifx_error() . ' ' . @ifx_errormsg();
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones.
+ *
+ * Requires that the DB implementation's constructor fills
+ * in the <var>$errorcode_map</var> property.
+ *
+ * @param string $nativecode error code returned by the database
+ * @return int a portable DB error code, or DB_ERROR if this DB
+ * implementation has no mapping for the given error code.
+ */
+ function errorCode($nativecode)
+ {
+ if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
+ $code = $match[1];
+ if (isset($this->errorcode_map[$code])) {
+ return $this->errorcode_map[$code];
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' if <var>$result</var> is a table name.
+ *
+ * If analyzing a query result and the result has duplicate field names,
+ * an error will be raised saying
+ * <samp>can't distinguish duplicate field names</samp>.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.6.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ $flds = @ifx_fieldproperties($id);
+ $count = @ifx_num_fields($id);
+
+ if (count($flds) != $count) {
+ return $this->raiseError("can't distinguish duplicate field names");
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $i = 0;
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ foreach ($flds as $key => $value) {
+ $props = explode(';', $value);
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func($key),
+ 'type' => $props[0],
+ 'len' => $props[1],
+ 'flags' => $props[4] == 'N' ? 'not_null' : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ $i++;
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @ifx_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT tabname FROM systables WHERE tabid >= 100';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's msql extension
+ * for interacting with Mini SQL databases
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: msql.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's msql extension
+ * for interacting with Mini SQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ * @since Class not functional until Release 1.7.0
+ */
+class DB_msql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'msql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'msql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * The query result resource created by PHP
+ *
+ * Used to make affectedRows() work. Only contains the result for
+ * data manipulation queries. Contains false for other queries.
+ *
+ * @var resource
+ * @access private
+ */
+ var $_result;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_msql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * Example of how to connect:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * // $dsn = 'msql://hostname/dbname'; // use a TCP connection
+ * $dsn = 'msql:///dbname'; // use a socket
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('msql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array();
+ if ($dsn['hostspec']) {
+ $params[] = $dsn['port']
+ ? $dsn['hostspec'] . ',' . $dsn['port']
+ : $dsn['hostspec'];
+ }
+
+ $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ if (($err = @msql_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if (!@msql_select_db($dsn['database'], $this->connection)) {
+ return $this->msqlRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @msql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @msql_query($query, $this->connection);
+ if (!$result) {
+ return $this->msqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if (DB::isManip($query)) {
+ $this->_result = $result;
+ return DB_OK;
+ } else {
+ $this->_result = false;
+ return $result;
+ }
+ }
+
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal msql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@msql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @msql_fetch_array($result, MSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @msql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @msql_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @msql_num_fields($result);
+ if (!$cols) {
+ return $this->msqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @msql_num_rows($result);
+ if ($rows === false) {
+ return $this->msqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affected()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (!$this->_result) {
+ return 0;
+ }
+ return msql_affected_rows($this->_result);
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_msql::createSequence(), DB_msql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = false;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result =& $this->query("SELECT _seq FROM ${seqname}");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = true;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * Also creates a new table to associate the sequence with. Uses
+ * a separate table to ensure portability with other drivers.
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_msql::nextID(), DB_msql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER NOT NULL)');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ $res = $this->query("CREATE SEQUENCE ON ${seqname}");
+ return $res;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_msql::nextID(), DB_msql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * mSQL does not support delimited identifiers
+ *
+ * @param string $str the identifier name to be quoted
+ *
+ * @return object a DB_Error object
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.7.0
+ */
+ function quoteIdentifier($str)
+ {
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.7.0
+ */
+ function escapeSimple($str)
+ {
+ return addslashes($str);
+ }
+
+ // }}}
+ // {{{ msqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_msql::errorNative(), DB_msql::errorCode()
+ */
+ function msqlRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @msql_error();
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message
+ *
+ * @param string $errormsg the error message returned from the database
+ *
+ * @return integer the error number from a DB_ERROR* constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/^Access to database denied/i'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^Bad index name/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/^Bad order field/i'
+ => DB_ERROR_SYNTAX,
+ '/^Bad type for comparison/i'
+ => DB_ERROR_SYNTAX,
+ '/^Can\'t perform LIKE on/i'
+ => DB_ERROR_SYNTAX,
+ '/^Can\'t use TEXT fields in LIKE comparison/i'
+ => DB_ERROR_SYNTAX,
+ '/^Couldn\'t create temporary table/i'
+ => DB_ERROR_CANNOT_CREATE,
+ '/^Error creating table file/i'
+ => DB_ERROR_CANNOT_CREATE,
+ '/^Field .* cannot be null$/i'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/^Index (field|condition) .* cannot be null$/i'
+ => DB_ERROR_SYNTAX,
+ '/^Invalid date format/i'
+ => DB_ERROR_INVALID_DATE,
+ '/^Invalid time format/i'
+ => DB_ERROR_INVALID,
+ '/^Literal value for .* is wrong type$/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/^No Database Selected/i'
+ => DB_ERROR_NODBSELECTED,
+ '/^No value specified for field/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '/^Non unique value for unique index/i'
+ => DB_ERROR_CONSTRAINT,
+ '/^Out of memory for temporary table/i'
+ => DB_ERROR_CANNOT_CREATE,
+ '/^Permission denied/i'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^Reference to un-selected table/i'
+ => DB_ERROR_SYNTAX,
+ '/^syntax error/i'
+ => DB_ERROR_SYNTAX,
+ '/^Table .* exists$/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/^Unknown database/i'
+ => DB_ERROR_NOSUCHDB,
+ '/^Unknown field/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/^Unknown (index|system variable)/i'
+ => DB_ERROR_NOT_FOUND,
+ '/^Unknown table/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/^Unqualified field/i'
+ => DB_ERROR_SYNTAX,
+ );
+ }
+
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @msql_query("SELECT * FROM $result",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @msql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $tmp = @msql_fetch_field($id);
+
+ $flags = '';
+ if ($tmp->not_null) {
+ $flags .= 'not_null ';
+ }
+ if ($tmp->unique) {
+ $flags .= 'unique_key ';
+ }
+ $flags = trim($flags);
+
+ $res[$i] = array(
+ 'table' => $case_func($tmp->table),
+ 'name' => $case_func($tmp->name),
+ 'type' => $tmp->type,
+ 'len' => msql_field_len($id, $i),
+ 'flags' => $flags,
+ );
+
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @msql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtain a list of a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return array the array containing the list of objects requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'databases':
+ $id = @msql_list_dbs($this->connection);
+ break;
+ case 'tables':
+ $id = @msql_list_tables($this->dsn['database'],
+ $this->connection);
+ break;
+ default:
+ return null;
+ }
+ if (!$id) {
+ return $this->msqlRaiseError();
+ }
+ $out = array();
+ while ($row = @msql_fetch_row($id)) {
+ $out[] = $row[0];
+ }
+ return $out;
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mssql extension
+ * for interacting with Microsoft SQL Server databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mssql.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mssql extension
+ * for interacting with Microsoft SQL Server databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_mssql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mssql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mssql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
+ var $errorcode_map = array(
+ 110 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 155 => DB_ERROR_NOSUCHFIELD,
+ 170 => DB_ERROR_SYNTAX,
+ 207 => DB_ERROR_NOSUCHFIELD,
+ 208 => DB_ERROR_NOSUCHTABLE,
+ 245 => DB_ERROR_INVALID_NUMBER,
+ 515 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 547 => DB_ERROR_CONSTRAINT,
+ 1913 => DB_ERROR_ALREADY_EXISTS,
+ 2627 => DB_ERROR_CONSTRAINT,
+ 2714 => DB_ERROR_ALREADY_EXISTS,
+ 3701 => DB_ERROR_NOSUCHTABLE,
+ 8134 => DB_ERROR_DIVZERO,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = null;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mssql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase')
+ && !PEAR::loadExtension('sybase_ct'))
+ {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array(
+ $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
+ $dsn['username'] ? $dsn['username'] : null,
+ $dsn['password'] ? $dsn['password'] : null,
+ );
+ if ($dsn['port']) {
+ $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
+ . $dsn['port'];
+ }
+
+ $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
+
+ $this->connection = @call_user_func_array($connect_function, $params);
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ @mssql_get_last_message());
+ }
+ if ($dsn['database']) {
+ if (!@mssql_select_db($dsn['database'], $this->connection)) {
+ return $this->raiseError(DB_ERROR_NODBSELECTED,
+ null, null, null,
+ @mssql_get_last_message());
+ }
+ $this->_db = $dsn['database'];
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mssql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mssql_query('BEGIN TRAN', $this->connection);
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @mssql_query($query, $this->connection);
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ return $ismanip ? DB_OK : $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mssql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return @mssql_next_result($result);
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mssql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mssql_fetch_array($result, MSSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mssql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @mssql_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mssql_num_fields($result);
+ if (!$cols) {
+ return $this->mssqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mssql_num_rows($result);
+ if ($rows === false) {
+ return $this->mssqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @mssql_query('COMMIT TRAN', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @mssql_query('ROLLBACK TRAN', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mssqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ $res = @mssql_query('select @@rowcount', $this->connection);
+ if (!$res) {
+ return $this->mssqlRaiseError();
+ }
+ $ar = @mssql_fetch_row($res);
+ if (!$ar) {
+ $result = 0;
+ } else {
+ @mssql_free_result($res);
+ $result = $ar[0];
+ }
+ } else {
+ $result = 0;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mssql::createSequence(), DB_mssql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
+ {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } elseif (!DB::isError($result)) {
+ $result =& $this->query("SELECT @@IDENTITY FROM $seqname");
+ $repeat = 0;
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ return $result[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mssql::nextID(), DB_mssql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE TABLE '
+ . $this->getSequenceName($seq_name)
+ . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
+ . ' [vapor] [int] NULL)');
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mssql::nextID(), DB_mssql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '[' . str_replace(']', ']]', $str) . ']';
+ }
+
+ // }}}
+ // {{{ mssqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mssql::errorNative(), DB_mssql::errorCode()
+ */
+ function mssqlRaiseError($code = null)
+ {
+ $message = @mssql_get_last_message();
+ if (!$code) {
+ $code = $this->errorNative();
+ }
+ return $this->raiseError($this->errorCode($code, $message),
+ null, null, null, "$code - $message");
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
+ if (!$res) {
+ return DB_ERROR;
+ }
+ $row = @mssql_fetch_row($res);
+ return $row[0];
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from mssql's native codes.
+ *
+ * If <var>$nativecode</var> isn't known yet, it will be looked up.
+ *
+ * @param mixed $nativecode mssql error code, if known
+ * @return integer an error number from a DB error constant
+ * @see errorNative()
+ */
+ function errorCode($nativecode = null, $msg = '')
+ {
+ if (!$nativecode) {
+ $nativecode = $this->errorNative();
+ }
+ if (isset($this->errorcode_map[$nativecode])) {
+ if ($nativecode == 3701
+ && preg_match('/Cannot drop the index/i', $msg))
+ {
+ return DB_ERROR_NOT_FOUND;
+ }
+ return $this->errorcode_map[$nativecode];
+ } else {
+ return DB_ERROR;
+ }
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ if (!@mssql_select_db($this->_db, $this->connection)) {
+ return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $id = @mssql_query("SELECT * FROM $result WHERE 1=0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mssql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@mssql_field_name($id, $i)),
+ 'type' => @mssql_field_type($id, $i),
+ 'len' => @mssql_field_length($id, $i),
+ // We only support flags for table
+ 'flags' => $got_string
+ ? $this->_mssql_field_flags($result,
+ @mssql_field_name($id, $i))
+ : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mssql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _mssql_field_flags()
+
+ /**
+ * Get a column's flags
+ *
+ * Supports "not_null", "primary_key",
+ * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
+ * "unique_key" (mssql unique index, unique check or primary_key) and
+ * "multiple_key" (multikey index)
+ *
+ * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
+ * not useful at all - is the behaviour of mysql_field_flags that primary
+ * keys are alway unique? is the interpretation of multiple_key correct?
+ *
+ * @param string $table the table name
+ * @param string $column the field name
+ *
+ * @return string the flags
+ *
+ * @access private
+ * @author Joern Barthel <j_barthel@web.de>
+ */
+ function _mssql_field_flags($table, $column)
+ {
+ static $tableName = null;
+ static $flags = array();
+
+ if ($table != $tableName) {
+
+ $flags = array();
+ $tableName = $table;
+
+ // get unique and primary keys
+ $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC);
+
+ foreach ($res as $val) {
+ $keys = explode(', ', $val['index_keys']);
+
+ if (sizeof($keys) > 1) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'multiple_key');
+ }
+ }
+
+ if (strpos($val['index_description'], 'primary key')) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'primary_key');
+ }
+ } elseif (strpos($val['index_description'], 'unique')) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'unique_key');
+ }
+ }
+ }
+
+ // get auto_increment, not_null and timestamp
+ $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC);
+
+ foreach ($res as $val) {
+ $val = array_change_key_case($val, CASE_LOWER);
+ if ($val['nullable'] == '0') {
+ $this->_add_flag($flags[$val['column_name']], 'not_null');
+ }
+ if (strpos($val['type_name'], 'identity')) {
+ $this->_add_flag($flags[$val['column_name']], 'auto_increment');
+ }
+ if (strpos($val['type_name'], 'timestamp')) {
+ $this->_add_flag($flags[$val['column_name']], 'timestamp');
+ }
+ }
+ }
+
+ if (array_key_exists($column, $flags)) {
+ return(implode(' ', $flags[$column]));
+ }
+ return '';
+ }
+
+ // }}}
+ // {{{ _add_flag()
+
+ /**
+ * Adds a string to the flags array if the flag is not yet in there
+ * - if there is no flag present the array is created
+ *
+ * @param array &$array the reference to the flag-array
+ * @param string $value the flag value
+ *
+ * @return void
+ *
+ * @access private
+ * @author Joern Barthel <j_barthel@web.de>
+ */
+ function _add_flag(&$array, $value)
+ {
+ if (!is_array($array)) {
+ $array = array($value);
+ } elseif (!in_array($value, $array)) {
+ array_push($array, $value);
+ }
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return "SELECT name FROM sysobjects WHERE type = 'U'"
+ . ' ORDER BY name';
+ case 'views':
+ return "SELECT name FROM sysobjects WHERE type = 'V'";
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mysql extension
+ * for interacting with MySQL databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mysql.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mysql extension
+ * for interacting with MySQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_mysql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mysql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mysql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '4.2.0',
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1004 => DB_ERROR_CANNOT_CREATE,
+ 1005 => DB_ERROR_CANNOT_CREATE,
+ 1006 => DB_ERROR_CANNOT_CREATE,
+ 1007 => DB_ERROR_ALREADY_EXISTS,
+ 1008 => DB_ERROR_CANNOT_DROP,
+ 1022 => DB_ERROR_ALREADY_EXISTS,
+ 1044 => DB_ERROR_ACCESS_VIOLATION,
+ 1046 => DB_ERROR_NODBSELECTED,
+ 1048 => DB_ERROR_CONSTRAINT,
+ 1049 => DB_ERROR_NOSUCHDB,
+ 1050 => DB_ERROR_ALREADY_EXISTS,
+ 1051 => DB_ERROR_NOSUCHTABLE,
+ 1054 => DB_ERROR_NOSUCHFIELD,
+ 1061 => DB_ERROR_ALREADY_EXISTS,
+ 1062 => DB_ERROR_ALREADY_EXISTS,
+ 1064 => DB_ERROR_SYNTAX,
+ 1091 => DB_ERROR_NOT_FOUND,
+ 1100 => DB_ERROR_NOT_LOCKED,
+ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 1142 => DB_ERROR_ACCESS_VIOLATION,
+ 1146 => DB_ERROR_NOSUCHTABLE,
+ 1216 => DB_ERROR_CONSTRAINT,
+ 1217 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mysql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's mysql driver supports the following extra DSN options:
+ * + new_link If set to true, causes subsequent calls to connect()
+ * to return a new connection link instead of the
+ * existing one. WARNING: this is not portable to
+ * other DBMS's. Available since PEAR DB 1.7.0.
+ * + client_flags Any combination of MYSQL_CLIENT_* constants.
+ * Only used if PHP is at version 4.3.0 or greater.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mysql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array();
+ if ($dsn['protocol'] && $dsn['protocol'] == 'unix') {
+ $params[0] = ':' . $dsn['socket'];
+ } else {
+ $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
+ : 'localhost';
+ if ($dsn['port']) {
+ $params[0] .= ':' . $dsn['port'];
+ }
+ }
+ $params[] = $dsn['username'] ? $dsn['username'] : null;
+ $params[] = $dsn['password'] ? $dsn['password'] : null;
+
+ if (!$persistent) {
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ $params[] = true;
+ } else {
+ $params[] = false;
+ }
+ }
+ if (version_compare(phpversion(), '4.3.0', '>=')) {
+ $params[] = isset($dsn['client_flags'])
+ ? $dsn['client_flags'] : null;
+ }
+
+ $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ if (($err = @mysql_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if ($dsn['database']) {
+ if (!@mysql_select_db($dsn['database'], $this->connection)) {
+ return $this->mysqlRaiseError();
+ }
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mysql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * Generally uses mysql_query(). If you want to use
+ * mysql_unbuffered_query() set the "result_buffering" option to 0 using
+ * setOptions(). This option was added in Release 1.7.0.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
+ $result = @mysql_query('BEGIN', $this->connection);
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ if (!$this->options['result_buffering']) {
+ $result = @mysql_unbuffered_query($query, $this->connection);
+ } else {
+ $result = @mysql_query($query, $this->connection);
+ }
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ if (is_resource($result)) {
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mysql result pointer to the next available result
+ *
+ * This method has not been implemented yet.
+ *
+ * @param a valid sql result resource
+ *
+ * @return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mysql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mysql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @mysql_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mysql_num_fields($result);
+ if (!$cols) {
+ return $this->mysqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mysql_num_rows($result);
+ if ($rows === null) {
+ return $this->mysqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysql_query('COMMIT', $this->connection);
+ $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysql_query('ROLLBACK', $this->connection);
+ $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ return @mysql_affected_rows($this->connection);
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mysql::createSequence(), DB_mysql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("UPDATE ${seqname} ".
+ 'SET id=LAST_INSERT_ID(id+1)');
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ // COMMON CASE
+ $id = @mysql_insert_id($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+ // EMPTY SEQ TABLE
+ // Sequence table must be empty for some reason, so fill
+ // it and return 1 and obtain a user-level lock
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ if ($result == 0) {
+ // Failed to get the lock
+ return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ // add the default value
+ $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ // Release the lock
+ $result = $this->getOne('SELECT RELEASE_LOCK('
+ . "'${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ // We know what the result will be, so no need to try again
+ return 1;
+
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ // ONDEMAND TABLE CREATION
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ $repeat = 1;
+ }
+
+ } elseif (DB::isError($result) &&
+ $result->getCode() == DB_ERROR_ALREADY_EXISTS)
+ {
+ // BACKWARDS COMPAT
+ // see _BCsequence() comment
+ $result = $this->_BCsequence($seqname);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $repeat = 1;
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // insert yields value 1, nextId call will generate ID 2
+ $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // so reset to zero
+ return $this->query("UPDATE ${seqname} SET id = 0");
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ _BCsequence()
+
+ /**
+ * Backwards compatibility with old sequence emulation implementation
+ * (clean up the dupes)
+ *
+ * @param string $seqname the sequence name to clean up
+ *
+ * @return bool true on success. A DB_Error object on failure.
+ *
+ * @access private
+ */
+ function _BCsequence($seqname)
+ {
+ // Obtain a user-level lock... this will release any previous
+ // application locks, but unlike LOCK TABLES, it does not abort
+ // the current transaction and is much less frequently used.
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ if ($result == 0) {
+ // Failed to get the lock, can't do the conversion, bail
+ // with a DB_ERROR_NOT_LOCKED error
+ return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
+ if (DB::isError($highest_id)) {
+ return $highest_id;
+ }
+ // This should kill all rows except the highest
+ // We should probably do something if $highest_id isn't
+ // numeric, but I'm at a loss as how to handle that...
+ $result = $this->query('DELETE FROM ' . $seqname
+ . " WHERE id <> $highest_id");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ // If another thread has been waiting for this lock,
+ // it will go thru the above procedure, but will have no
+ // real effect
+ $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * MySQL can't handle the backtick character (<kbd>`</kbd>) in
+ * table or column names.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '`' . $str . '`';
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ if (function_exists('mysql_real_escape_string')) {
+ return @mysql_real_escape_string($str, $this->connection);
+ } else {
+ return @mysql_escape_string($str);
+ }
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * This little hack lets you know how many rows were deleted
+ * when running a "DELETE FROM table" query. Only implemented
+ * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_common::setOption()
+ */
+ function modifyQuery($query)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
+ // "DELETE FROM table" gives 0 affected rows in MySQL.
+ // This little hack lets you know how many rows were deleted.
+ if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+ $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+ 'DELETE FROM \1 WHERE 1=1', $query);
+ }
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query)) {
+ return $query . " LIMIT $count";
+ } else {
+ return $query . " LIMIT $from, $count";
+ }
+ }
+
+ // }}}
+ // {{{ mysqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mysql::errorNative(), DB_common::errorCode()
+ */
+ function mysqlRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
+ $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
+ }
+ $errno = $this->errorCode(mysql_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @mysql_errno($this->connection) . ' ** ' .
+ @mysql_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @mysql_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @mysql_list_fields($this->dsn['database'],
+ $result, $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mysql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $case_func(@mysql_field_table($id, $i)),
+ 'name' => $case_func(@mysql_field_name($id, $i)),
+ 'type' => @mysql_field_type($id, $i),
+ 'len' => @mysql_field_len($id, $i),
+ 'flags' => @mysql_field_flags($id, $i),
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mysql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SHOW TABLES';
+ case 'users':
+ return 'SELECT DISTINCT User FROM mysql.user';
+ case 'databases':
+ return 'SHOW DATABASES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mysqli extension
+ * for interacting with MySQL databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mysqli.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mysqli extension
+ * for interacting with MySQL databases
+ *
+ * This is for MySQL versions 4.1 and above. Requires PHP 5.
+ *
+ * Note that persistent connections no longer exist.
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ * @since Class functional since Release 1.6.3
+ */
+class DB_mysqli extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mysqli';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mysqli';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => false,
+ 'prepare' => false,
+ 'ssl' => true,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1004 => DB_ERROR_CANNOT_CREATE,
+ 1005 => DB_ERROR_CANNOT_CREATE,
+ 1006 => DB_ERROR_CANNOT_CREATE,
+ 1007 => DB_ERROR_ALREADY_EXISTS,
+ 1008 => DB_ERROR_CANNOT_DROP,
+ 1022 => DB_ERROR_ALREADY_EXISTS,
+ 1044 => DB_ERROR_ACCESS_VIOLATION,
+ 1046 => DB_ERROR_NODBSELECTED,
+ 1048 => DB_ERROR_CONSTRAINT,
+ 1049 => DB_ERROR_NOSUCHDB,
+ 1050 => DB_ERROR_ALREADY_EXISTS,
+ 1051 => DB_ERROR_NOSUCHTABLE,
+ 1054 => DB_ERROR_NOSUCHFIELD,
+ 1061 => DB_ERROR_ALREADY_EXISTS,
+ 1062 => DB_ERROR_ALREADY_EXISTS,
+ 1064 => DB_ERROR_SYNTAX,
+ 1091 => DB_ERROR_NOT_FOUND,
+ 1100 => DB_ERROR_NOT_LOCKED,
+ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 1142 => DB_ERROR_ACCESS_VIOLATION,
+ 1146 => DB_ERROR_NOSUCHTABLE,
+ 1216 => DB_ERROR_CONSTRAINT,
+ 1217 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+ /**
+ * Array for converting MYSQLI_*_FLAG constants to text values
+ * @var array
+ * @access public
+ * @since Property available since Release 1.6.5
+ */
+ var $mysqli_flags = array(
+ MYSQLI_NOT_NULL_FLAG => 'not_null',
+ MYSQLI_PRI_KEY_FLAG => 'primary_key',
+ MYSQLI_UNIQUE_KEY_FLAG => 'unique_key',
+ MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key',
+ MYSQLI_BLOB_FLAG => 'blob',
+ MYSQLI_UNSIGNED_FLAG => 'unsigned',
+ MYSQLI_ZEROFILL_FLAG => 'zerofill',
+ MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment',
+ MYSQLI_TIMESTAMP_FLAG => 'timestamp',
+ MYSQLI_SET_FLAG => 'set',
+ // MYSQLI_NUM_FLAG => 'numeric', // unnecessary
+ // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie
+ MYSQLI_GROUP_FLAG => 'group_by'
+ );
+
+ /**
+ * Array for converting MYSQLI_TYPE_* constants to text values
+ * @var array
+ * @access public
+ * @since Property available since Release 1.6.5
+ */
+ var $mysqli_types = array(
+ MYSQLI_TYPE_DECIMAL => 'decimal',
+ MYSQLI_TYPE_TINY => 'tinyint',
+ MYSQLI_TYPE_SHORT => 'int',
+ MYSQLI_TYPE_LONG => 'int',
+ MYSQLI_TYPE_FLOAT => 'float',
+ MYSQLI_TYPE_DOUBLE => 'double',
+ // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it
+ MYSQLI_TYPE_TIMESTAMP => 'timestamp',
+ MYSQLI_TYPE_LONGLONG => 'bigint',
+ MYSQLI_TYPE_INT24 => 'mediumint',
+ MYSQLI_TYPE_DATE => 'date',
+ MYSQLI_TYPE_TIME => 'time',
+ MYSQLI_TYPE_DATETIME => 'datetime',
+ MYSQLI_TYPE_YEAR => 'year',
+ MYSQLI_TYPE_NEWDATE => 'date',
+ MYSQLI_TYPE_ENUM => 'enum',
+ MYSQLI_TYPE_SET => 'set',
+ MYSQLI_TYPE_TINY_BLOB => 'tinyblob',
+ MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
+ MYSQLI_TYPE_LONG_BLOB => 'longblob',
+ MYSQLI_TYPE_BLOB => 'blob',
+ MYSQLI_TYPE_VAR_STRING => 'varchar',
+ MYSQLI_TYPE_STRING => 'char',
+ MYSQLI_TYPE_GEOMETRY => 'geometry',
+ );
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mysqli()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's mysqli driver supports the following extra DSN options:
+ * + When the 'ssl' $option passed to DB::connect() is true:
+ * + key The path to the key file.
+ * + cert The path to the certificate file.
+ * + ca The path to the certificate authority file.
+ * + capath The path to a directory that contains trusted SSL
+ * CA certificates in pem format.
+ * + cipher The list of allowable ciphers for SSL encryption.
+ *
+ * Example of how to connect using SSL:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = array(
+ * 'phptype' => 'mysqli',
+ * 'username' => 'someuser',
+ * 'password' => 'apasswd',
+ * 'hostspec' => 'localhost',
+ * 'database' => 'thedb',
+ * 'key' => 'client-key.pem',
+ * 'cert' => 'client-cert.pem',
+ * 'ca' => 'cacert.pem',
+ * 'capath' => '/path/to/ca/dir',
+ * 'cipher' => 'AES',
+ * );
+ *
+ * $options = array(
+ * 'ssl' => true,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mysqli')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $ini = ini_get('track_errors');
+ ini_set('track_errors', 1);
+ $php_errormsg = '';
+
+ if ($this->getOption('ssl') === true) {
+ $init = mysqli_init();
+ mysqli_ssl_set(
+ $init,
+ empty($dsn['key']) ? null : $dsn['key'],
+ empty($dsn['cert']) ? null : $dsn['cert'],
+ empty($dsn['ca']) ? null : $dsn['ca'],
+ empty($dsn['capath']) ? null : $dsn['capath'],
+ empty($dsn['cipher']) ? null : $dsn['cipher']
+ );
+ if ($this->connection = @mysqli_real_connect(
+ $init,
+ $dsn['hostspec'],
+ $dsn['username'],
+ $dsn['password'],
+ $dsn['database'],
+ $dsn['port'],
+ $dsn['socket']))
+ {
+ $this->connection = $init;
+ }
+ } else {
+ $this->connection = @mysqli_connect(
+ $dsn['hostspec'],
+ $dsn['username'],
+ $dsn['password'],
+ $dsn['database'],
+ $dsn['port'],
+ $dsn['socket']
+ );
+ }
+
+ ini_set('track_errors', $ini);
+
+ if (!$this->connection) {
+ if (($err = @mysqli_connect_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if ($dsn['database']) {
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mysqli_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0');
+ $result = @mysqli_query($this->connection, 'BEGIN');
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @mysqli_query($this->connection, $query);
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ if (is_object($result)) {
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mysql result pointer to the next available result.
+ *
+ * This method has not been implemented yet.
+ *
+ * @param resource $result a valid sql result resource
+ * @return false
+ * @access public
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mysqli_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mysqli_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @mysqli_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mysqli_num_fields($result);
+ if (!$cols) {
+ return $this->mysqliRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mysqli_num_rows($result);
+ if ($rows === null) {
+ return $this->mysqliRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysqli_query($this->connection, 'COMMIT');
+ $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysqli_select_db($this->connection, $this->_db)) {
+ return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysqli_query($this->connection, 'ROLLBACK');
+ $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqliRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ return @mysqli_affected_rows($this->connection);
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mysqli::createSequence(), DB_mysqli::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query('UPDATE ' . $seqname
+ . ' SET id = LAST_INSERT_ID(id + 1)');
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ // COMMON CASE
+ $id = @mysqli_insert_id($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+
+ // EMPTY SEQ TABLE
+ // Sequence table must be empty for some reason,
+ // so fill it and return 1
+ // Obtain a user-level lock
+ $result = $this->getOne('SELECT GET_LOCK('
+ . "'${seqname}_lock', 10)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ if ($result == 0) {
+ return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ // add the default value
+ $result = $this->query('REPLACE INTO ' . $seqname
+ . ' (id) VALUES (0)');
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ // Release the lock
+ $result = $this->getOne('SELECT RELEASE_LOCK('
+ . "'${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ // We know what the result will be, so no need to try again
+ return 1;
+
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ // ONDEMAND TABLE CREATION
+ $result = $this->createSequence($seq_name);
+
+ // Since createSequence initializes the ID to be 1,
+ // we do not need to retrieve the ID again (or we will get 2)
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ // First ID of a newly created sequence is 1
+ return 1;
+ }
+
+ } elseif (DB::isError($result) &&
+ $result->getCode() == DB_ERROR_ALREADY_EXISTS)
+ {
+ // BACKWARDS COMPAT
+ // see _BCsequence() comment
+ $result = $this->_BCsequence($seqname);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $repeat = 1;
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mysqli::nextID(), DB_mysqli::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // insert yields value 1, nextId call will generate ID 2
+ return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ _BCsequence()
+
+ /**
+ * Backwards compatibility with old sequence emulation implementation
+ * (clean up the dupes)
+ *
+ * @param string $seqname the sequence name to clean up
+ *
+ * @return bool true on success. A DB_Error object on failure.
+ *
+ * @access private
+ */
+ function _BCsequence($seqname)
+ {
+ // Obtain a user-level lock... this will release any previous
+ // application locks, but unlike LOCK TABLES, it does not abort
+ // the current transaction and is much less frequently used.
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ if ($result == 0) {
+ // Failed to get the lock, can't do the conversion, bail
+ // with a DB_ERROR_NOT_LOCKED error
+ return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
+ if (DB::isError($highest_id)) {
+ return $highest_id;
+ }
+
+ // This should kill all rows except the highest
+ // We should probably do something if $highest_id isn't
+ // numeric, but I'm at a loss as how to handle that...
+ $result = $this->query('DELETE FROM ' . $seqname
+ . " WHERE id <> $highest_id");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ // If another thread has been waiting for this lock,
+ // it will go thru the above procedure, but will have no
+ // real effect
+ $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * MySQL can't handle the backtick character (<kbd>`</kbd>) in
+ * table or column names.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '`' . $str . '`';
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return @mysqli_real_escape_string($this->connection, $str);
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query)) {
+ return $query . " LIMIT $count";
+ } else {
+ return $query . " LIMIT $from, $count";
+ }
+ }
+
+ // }}}
+ // {{{ mysqliRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mysqli::errorNative(), DB_common::errorCode()
+ */
+ function mysqliRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
+ $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
+ }
+ $errno = $this->errorCode(mysqli_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @mysqli_errno($this->connection) . ' ** ' .
+ @mysqli_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @mysqli_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @mysqli_query($this->connection,
+ "SELECT * FROM $result LIMIT 0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_a($id, 'mysqli_result')) {
+ return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mysqli_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $tmp = @mysqli_fetch_field($id);
+
+ $flags = '';
+ foreach ($this->mysqli_flags as $const => $means) {
+ if ($tmp->flags & $const) {
+ $flags .= $means . ' ';
+ }
+ }
+ if ($tmp->def) {
+ $flags .= 'default_' . rawurlencode($tmp->def);
+ }
+ $flags = trim($flags);
+
+ $res[$i] = array(
+ 'table' => $case_func($tmp->table),
+ 'name' => $case_func($tmp->name),
+ 'type' => isset($this->mysqli_types[$tmp->type])
+ ? $this->mysqli_types[$tmp->type]
+ : 'unknown',
+ 'len' => $tmp->max_length,
+ 'flags' => $flags,
+ );
+
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mysqli_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SHOW TABLES';
+ case 'users':
+ return 'SELECT DISTINCT User FROM mysql.user';
+ case 'databases':
+ return 'SHOW DATABASES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's oci8 extension
+ * for interacting with Oracle databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author James L. Pine <jlp@valinux.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: oci8.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's oci8 extension
+ * for interacting with Oracle databases
+ *
+ * Definitely works with versions 8 and 9 of Oracle.
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * Be aware... OCIError() only appears to return anything when given a
+ * statement, so functions return the generic DB_ERROR instead of more
+ * useful errors that have to do with feedback from the database.
+ *
+ * @category Database
+ * @package DB
+ * @author James L. Pine <jlp@valinux.com>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_oci8 extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'oci8';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'oci8';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '5.0.0',
+ 'numrows' => 'subquery',
+ 'pconnect' => true,
+ 'prepare' => true,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1 => DB_ERROR_CONSTRAINT,
+ 900 => DB_ERROR_SYNTAX,
+ 904 => DB_ERROR_NOSUCHFIELD,
+ 913 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 921 => DB_ERROR_SYNTAX,
+ 923 => DB_ERROR_SYNTAX,
+ 942 => DB_ERROR_NOSUCHTABLE,
+ 955 => DB_ERROR_ALREADY_EXISTS,
+ 1400 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 1401 => DB_ERROR_INVALID,
+ 1407 => DB_ERROR_CONSTRAINT_NOT_NULL,
+ 1418 => DB_ERROR_NOT_FOUND,
+ 1476 => DB_ERROR_DIVZERO,
+ 1722 => DB_ERROR_INVALID_NUMBER,
+ 2289 => DB_ERROR_NOSUCHTABLE,
+ 2291 => DB_ERROR_CONSTRAINT,
+ 2292 => DB_ERROR_CONSTRAINT,
+ 2449 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * Stores the $data passed to execute() in the oci8 driver
+ *
+ * Gets reset to array() when simpleQuery() is run.
+ *
+ * Needed in case user wants to call numRows() after prepare/execute
+ * was used.
+ *
+ * @var array
+ * @access private
+ */
+ var $_data = array();
+
+ /**
+ * The result or statement handle from the most recently executed query
+ * @var resource
+ */
+ var $last_stmt;
+
+ /**
+ * Is the given prepared statement a data manipulation query?
+ * @var array
+ * @access private
+ */
+ var $manip_query = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_oci8()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * If PHP is at version 5.0.0 or greater:
+ * + Generally, oci_connect() or oci_pconnect() are used.
+ * + But if the new_link DSN option is set to true, oci_new_connect()
+ * is used.
+ *
+ * When using PHP version 4.x, OCILogon() or OCIPLogon() are used.
+ *
+ * PEAR DB's oci8 driver supports the following extra DSN options:
+ * + charset The character set to be used on the connection.
+ * Only used if PHP is at version 5.0.0 or greater
+ * and the Oracle server is at 9.2 or greater.
+ * Available since PEAR DB 1.7.0.
+ * + new_link If set to true, causes subsequent calls to
+ * connect() to return a new connection link
+ * instead of the existing one. WARNING: this is
+ * not portable to other DBMS's.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('oci8')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ if (function_exists('oci_connect')) {
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ $connect_function = 'oci_new_connect';
+ } else {
+ $connect_function = $persistent ? 'oci_pconnect'
+ : 'oci_connect';
+ }
+
+ // Backwards compatibility with DB < 1.7.0
+ if (empty($dsn['database']) && !empty($dsn['hostspec'])) {
+ $db = $dsn['hostspec'];
+ } else {
+ $db = $dsn['database'];
+ }
+
+ $char = empty($dsn['charset']) ? null : $dsn['charset'];
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password'],
+ $db,
+ $char);
+ $error = OCIError();
+ if (!empty($error) && $error['code'] == 12541) {
+ // Couldn't find TNS listener. Try direct connection.
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password'],
+ null,
+ $char);
+ }
+ } else {
+ $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
+ if ($dsn['hostspec']) {
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password'],
+ $dsn['hostspec']);
+ } elseif ($dsn['username'] || $dsn['password']) {
+ $this->connection = @$connect_function($dsn['username'],
+ $dsn['password']);
+ }
+ }
+
+ if (!$this->connection) {
+ $error = OCIError();
+ $error = (is_array($error)) ? $error['message'] : null;
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $error);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ if (function_exists('oci_close')) {
+ $ret = @oci_close($this->connection);
+ } else {
+ $ret = @OCILogOff($this->connection);
+ }
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * To determine how many rows of a result set get buffered using
+ * ocisetprefetch(), see the "result_buffering" option in setOptions().
+ * This option was added in Release 1.7.0.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->_data = array();
+ $this->last_parameters = array();
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @OCIParse($this->connection, $query);
+ if (!$result) {
+ return $this->oci8RaiseError();
+ }
+ if ($this->autocommit) {
+ $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS);
+ } else {
+ $success = @OCIExecute($result,OCI_DEFAULT);
+ }
+ if (!$success) {
+ return $this->oci8RaiseError($result);
+ }
+ $this->last_stmt = $result;
+ if (DB::isManip($query)) {
+ return DB_OK;
+ } else {
+ @ocisetprefetch($result, $this->options['result_buffering']);
+ return $result;
+ }
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal oracle result pointer to the next available result
+ *
+ * @param a valid oci8 result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE &&
+ $moredata)
+ {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+ }
+ if (!$moredata) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @OCIFreeStatement($result);
+ }
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_oci8::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ if (!is_resource($stmt)) {
+ return false;
+ }
+ if ($free_resource) {
+ @ocifreestatement($stmt);
+ }
+ if (isset($this->prepare_types[(int)$stmt])) {
+ unset($this->prepare_types[(int)$stmt]);
+ unset($this->manip_query[(int)$stmt]);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * Only works if the DB_PORTABILITY_NUMROWS portability option
+ * is turned on.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows(), DB_common::setOption()
+ */
+ function numRows($result)
+ {
+ // emulate numRows for Oracle. yuck.
+ if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
+ $result === $this->last_stmt)
+ {
+ $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')';
+ $save_query = $this->last_query;
+ $save_stmt = $this->last_stmt;
+
+ if (count($this->_data)) {
+ $smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')');
+ $count = $this->execute($smt, $this->_data);
+ } else {
+ $count =& $this->query($countquery);
+ }
+
+ if (DB::isError($count) ||
+ DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED)))
+ {
+ $this->last_query = $save_query;
+ $this->last_stmt = $save_stmt;
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ return $row[0];
+ }
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @OCINumCols($result);
+ if (!$cols) {
+ return $this->oci8RaiseError($result);
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute().
+ *
+ * With oci8, this is emulated.
+ *
+ * prepare() requires a generic query as string like <code>
+ * INSERT INTO numbers VALUES (?, ?, ?)
+ * </code>. The <kbd>?</kbd> characters are placeholders.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders. Example: <code>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </code>
+ *
+ * @param string $query the query to be prepared
+ *
+ * @return mixed DB statement resource on success. DB_Error on failure.
+ *
+ * @see DB_oci8::execute()
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $binds = count($tokens) - 1;
+ $token = 0;
+ $types = array();
+ $newquery = '';
+
+ foreach ($tokens as $key => $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ unset($tokens[$key]);
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ unset($tokens[$key]);
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ unset($tokens[$key]);
+ break;
+ default:
+ $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ if ($key != $binds) {
+ $newquery .= $tokens[$key] . ':bind' . $token;
+ } else {
+ $newquery .= $tokens[$key];
+ }
+ }
+ }
+
+ $this->last_query = $query;
+ $newquery = $this->modifyQuery($newquery);
+ if (!$stmt = @OCIParse($this->connection, $newquery)) {
+ return $this->oci8RaiseError();
+ }
+ $this->prepare_types[(int)$stmt] = $types;
+ $this->manip_query[(int)$stmt] = DB::isManip($query);
+ return $stmt;
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare().
+ *
+ * To determine how many rows of a result set get buffered using
+ * ocisetprefetch(), see the "result_buffering" option in setOptions().
+ * This option was added in Release 1.7.0.
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 for non-array items or the
+ * quantity of elements in the array.
+ *
+ * @return mixed returns an oic8 result resource for successful SELECT
+ * queries, DB_OK for other successful queries.
+ * A DB error object is returned on failure.
+ *
+ * @see DB_oci8::prepare()
+ */
+ function &execute($stmt, $data = array())
+ {
+ $data = (array)$data;
+ $this->last_parameters = $data;
+ $this->_data = $data;
+
+ $types =& $this->prepare_types[(int)$stmt];
+ if (count($types) != count($data)) {
+ $tmp =& $this->raiseError(DB_ERROR_MISMATCH);
+ return $tmp;
+ }
+
+ $i = 0;
+ foreach ($data as $key => $value) {
+ if ($types[$i] == DB_PARAM_MISC) {
+ /*
+ * Oracle doesn't seem to have the ability to pass a
+ * parameter along unchanged, so strip off quotes from start
+ * and end, plus turn two single quotes to one single quote,
+ * in order to avoid the quotes getting escaped by
+ * Oracle and ending up in the database.
+ */
+ $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
+ $data[$key] = str_replace("''", "'", $data[$key]);
+ } elseif ($types[$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($data[$key], 'rb');
+ if (!$fp) {
+ $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ return $tmp;
+ }
+ $data[$key] = fread($fp, filesize($data[$key]));
+ fclose($fp);
+ }
+ if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) {
+ $tmp = $this->oci8RaiseError($stmt);
+ return $tmp;
+ }
+ $i++;
+ }
+ if ($this->autocommit) {
+ $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
+ } else {
+ $success = @OCIExecute($stmt, OCI_DEFAULT);
+ }
+ if (!$success) {
+ $tmp = $this->oci8RaiseError($stmt);
+ return $tmp;
+ }
+ $this->last_stmt = $stmt;
+ if ($this->manip_query[(int)$stmt]) {
+ $tmp = DB_OK;
+ } else {
+ @ocisetprefetch($stmt, $this->options['result_buffering']);
+ $tmp =& new DB_result($this, $stmt);
+ }
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ $this->autocommit = (bool)$onoff;;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ $result = @OCICommit($this->connection);
+ if (!$result) {
+ return $this->oci8RaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ $result = @OCIRollback($this->connection);
+ if (!$result) {
+ return $this->oci8RaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if ($this->last_stmt === false) {
+ return $this->oci8RaiseError();
+ }
+ $result = @OCIRowCount($this->last_stmt);
+ if ($result === false) {
+ return $this->oci8RaiseError($this->last_stmt);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ */
+ function modifyQuery($query)
+ {
+ if (preg_match('/^\s*SELECT/i', $query) &&
+ !preg_match('/\sFROM\s/i', $query)) {
+ $query .= ' FROM dual';
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ // Let Oracle return the name of the columns instead of
+ // coding a "home" SQL parser
+
+ if (count($params)) {
+ $result = $this->prepare("SELECT * FROM ($query) "
+ . 'WHERE NULL = NULL');
+ $tmp =& $this->execute($result, $params);
+ } else {
+ $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
+
+ if (!$result = @OCIParse($this->connection, $q_fields)) {
+ $this->last_query = $q_fields;
+ return $this->oci8RaiseError();
+ }
+ if (!@OCIExecute($result, OCI_DEFAULT)) {
+ $this->last_query = $q_fields;
+ return $this->oci8RaiseError($result);
+ }
+ }
+
+ $ncols = OCINumCols($result);
+ $cols = array();
+ for ( $i = 1; $i <= $ncols; $i++ ) {
+ $cols[] = '"' . OCIColumnName($result, $i) . '"';
+ }
+ $fields = implode(', ', $cols);
+ // XXX Test that (tip by John Lim)
+ //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) {
+ // // Introduce the FIRST_ROWS Oracle query optimizer
+ // $query = substr($query, strlen($match[0]), strlen($query));
+ // $query = "SELECT /* +FIRST_ROWS */ " . $query;
+ //}
+
+ // Construct the query
+ // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
+ // Perhaps this could be optimized with the use of Unions
+ $query = "SELECT $fields FROM".
+ " (SELECT rownum as linenum, $fields FROM".
+ " ($query)".
+ ' WHERE rownum <= '. ($from + $count) .
+ ') WHERE linenum >= ' . ++$from;
+ return $query;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_oci8::createSequence(), DB_oci8::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = 0;
+ do {
+ $this->expectError(DB_ERROR_NOSUCHTABLE);
+ $result =& $this->query("SELECT ${seqname}.nextval FROM dual");
+ $this->popExpect();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ return $arr[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_oci8::nextID(), DB_oci8::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_oci8::nextID(), DB_oci8::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ oci8RaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_oci8::errorNative(), DB_oci8::errorCode()
+ */
+ function oci8RaiseError($errno = null)
+ {
+ if ($errno === null) {
+ $error = @OCIError($this->connection);
+ return $this->raiseError($this->errorCode($error['code']),
+ null, null, null, $error['message']);
+ } elseif (is_resource($errno)) {
+ $error = @OCIError($errno);
+ return $this->raiseError($this->errorCode($error['code']),
+ null, null, null, $error['message']);
+ }
+ return $this->raiseError($this->errorCode($errno));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code. FALSE if the code could not be
+ * determined
+ */
+ function errorNative()
+ {
+ if (is_resource($this->last_stmt)) {
+ $error = @OCIError($this->last_stmt);
+ } else {
+ $error = @OCIError($this->connection);
+ }
+ if (is_array($error)) {
+ return $error['code'];
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * NOTE: flags won't contain index information.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $res = array();
+
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $result = strtoupper($result);
+ $q_fields = 'SELECT column_name, data_type, data_length, '
+ . 'nullable '
+ . 'FROM user_tab_columns '
+ . "WHERE table_name='$result' ORDER BY column_id";
+
+ $this->last_query = $q_fields;
+
+ if (!$stmt = @OCIParse($this->connection, $q_fields)) {
+ return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+ if (!@OCIExecute($stmt, OCI_DEFAULT)) {
+ return $this->oci8RaiseError($stmt);
+ }
+
+ $i = 0;
+ while (@OCIFetch($stmt)) {
+ $res[$i] = array(
+ 'table' => $case_func($result),
+ 'name' => $case_func(@OCIResult($stmt, 1)),
+ 'type' => @OCIResult($stmt, 2),
+ 'len' => @OCIResult($stmt, 3),
+ 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ $i++;
+ }
+
+ if ($mode) {
+ $res['num_fields'] = $i;
+ }
+ @OCIFreeStatement($stmt);
+
+ } else {
+ if (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $result = $result->result;
+ }
+
+ $res = array();
+
+ if ($result === $this->last_stmt) {
+ $count = @OCINumCols($result);
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => '',
+ 'name' => $case_func(@OCIColumnName($result, $i+1)),
+ 'type' => @OCIColumnType($result, $i+1),
+ 'len' => @OCIColumnSize($result, $i+1),
+ 'flags' => '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+ } else {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT table_name FROM user_tables';
+ case 'synonyms':
+ return 'SELECT synonym_name FROM user_synonyms';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's odbc extension
+ * for interacting with databases via ODBC connections
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: odbc.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's odbc extension
+ * for interacting with databases via ODBC connections
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * More info on ODBC errors could be found here:
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_odbc extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'odbc';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'sql92';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * NOTE: The feature set of the following drivers are different than
+ * the default:
+ * + solid: 'transactions' = true
+ * + navision: 'limit' = false
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ '01004' => DB_ERROR_TRUNCATED,
+ '07001' => DB_ERROR_MISMATCH,
+ '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW,
+ '21S02' => DB_ERROR_MISMATCH,
+ '22001' => DB_ERROR_INVALID,
+ '22003' => DB_ERROR_INVALID_NUMBER,
+ '22005' => DB_ERROR_INVALID_NUMBER,
+ '22008' => DB_ERROR_INVALID_DATE,
+ '22012' => DB_ERROR_DIVZERO,
+ '23000' => DB_ERROR_CONSTRAINT,
+ '23502' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '23503' => DB_ERROR_CONSTRAINT,
+ '23504' => DB_ERROR_CONSTRAINT,
+ '23505' => DB_ERROR_CONSTRAINT,
+ '24000' => DB_ERROR_INVALID,
+ '34000' => DB_ERROR_INVALID,
+ '37000' => DB_ERROR_SYNTAX,
+ '42000' => DB_ERROR_SYNTAX,
+ '42601' => DB_ERROR_SYNTAX,
+ 'IM001' => DB_ERROR_UNSUPPORTED,
+ 'S0000' => DB_ERROR_NOSUCHTABLE,
+ 'S0001' => DB_ERROR_ALREADY_EXISTS,
+ 'S0002' => DB_ERROR_NOSUCHTABLE,
+ 'S0011' => DB_ERROR_ALREADY_EXISTS,
+ 'S0012' => DB_ERROR_NOT_FOUND,
+ 'S0021' => DB_ERROR_ALREADY_EXISTS,
+ 'S0022' => DB_ERROR_NOSUCHFIELD,
+ 'S1009' => DB_ERROR_INVALID,
+ 'S1090' => DB_ERROR_INVALID,
+ 'S1C00' => DB_ERROR_NOT_CAPABLE,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ * @access private
+ */
+ var $affected = 0;
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_odbc()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's odbc driver supports the following extra DSN options:
+ * + cursor The type of cursor to be used for this connection.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('odbc')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+ switch ($this->dbsyntax) {
+ case 'access':
+ case 'db2':
+ case 'solid':
+ $this->features['transactions'] = true;
+ break;
+ case 'navision':
+ $this->features['limit'] = false;
+ }
+
+ /*
+ * This is hear for backwards compatibility. Should have been using
+ * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used.
+ */
+ if ($dsn['database']) {
+ $odbcdsn = $dsn['database'];
+ } elseif ($dsn['hostspec']) {
+ $odbcdsn = $dsn['hostspec'];
+ } else {
+ $odbcdsn = 'localhost';
+ }
+
+ $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
+
+ if (empty($dsn['cursor'])) {
+ $this->connection = @$connect_function($odbcdsn, $dsn['username'],
+ $dsn['password']);
+ } else {
+ $this->connection = @$connect_function($odbcdsn, $dsn['username'],
+ $dsn['password'],
+ $dsn['cursor']);
+ }
+
+ if (!is_resource($this->connection)) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $this->errorNative());
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $err = @odbc_close($this->connection);
+ $this->connection = null;
+ return $err;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ $result = @odbc_exec($this->connection, $query);
+ if (!$result) {
+ return $this->odbcRaiseError(); // XXX ERRORMSG
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if (DB::isManip($query)) {
+ $this->affected = $result; // For affectedRows()
+ return DB_OK;
+ }
+ $this->affected = 0;
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal odbc result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return @odbc_next_result($result);
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ $arr = array();
+ if ($rownum !== null) {
+ $rownum++; // ODBC first row is 1
+ if (version_compare(phpversion(), '4.2.0', 'ge')) {
+ $cols = @odbc_fetch_into($result, $arr, $rownum);
+ } else {
+ $cols = @odbc_fetch_into($result, $rownum, $arr);
+ }
+ } else {
+ $cols = @odbc_fetch_into($result, $arr);
+ }
+ if (!$cols) {
+ return null;
+ }
+ if ($fetchmode !== DB_FETCHMODE_ORDERED) {
+ for ($i = 0; $i < count($arr); $i++) {
+ $colName = @odbc_field_name($result, $i+1);
+ $a[$colName] = $arr[$i];
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $a = array_change_key_case($a, CASE_LOWER);
+ }
+ $arr = $a;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @odbc_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @odbc_num_fields($result);
+ if (!$cols) {
+ return $this->odbcRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (empty($this->affected)) { // In case of SELECT stms
+ return 0;
+ }
+ $nrows = @odbc_num_rows($this->affected);
+ if ($nrows == -1) {
+ return $this->odbcRaiseError();
+ }
+ return $nrows;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * Not all ODBC drivers support this functionality. If they don't
+ * a DB_Error object for DB_ERROR_UNSUPPORTED is returned.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $nrows = @odbc_num_rows($result);
+ if ($nrows == -1) {
+ return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
+ }
+ if ($nrows === false) {
+ return $this->odbcRaiseError();
+ }
+ return $nrows;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked
+ * "Use ANSI quoted identifiers" when setting up the ODBC data source.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ switch ($this->dsn['dbsyntax']) {
+ case 'access':
+ return '[' . $str . ']';
+ case 'mssql':
+ case 'sybase':
+ return '[' . str_replace(']', ']]', $str) . ']';
+ case 'mysql':
+ case 'mysqli':
+ return '`' . $str . '`';
+ default:
+ return '"' . str_replace('"', '""', $str) . '"';
+ }
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ * @internal
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_odbc::createSequence(), DB_odbc::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("update ${seqname} set id = id + 1");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = 1;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $result = $this->query("insert into ${seqname} (id) values(0)");
+ } else {
+ $repeat = 0;
+ }
+ } while ($repeat);
+
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ $result = $this->query("select id from ${seqname}");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ if (DB::isError($row || !$row)) {
+ return $row;
+ }
+
+ return $row[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_odbc::nextID(), DB_odbc::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE TABLE '
+ . $this->getSequenceName($seq_name)
+ . ' (id integer NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_odbc::nextID(), DB_odbc::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ if (!@odbc_autocommit($this->connection, $onoff)) {
+ return $this->odbcRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if (!@odbc_commit($this->connection)) {
+ return $this->odbcRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if (!@odbc_rollback($this->connection)) {
+ return $this->odbcRaiseError();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ odbcRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_odbc::errorNative(), DB_common::errorCode()
+ */
+ function odbcRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ switch ($this->dbsyntax) {
+ case 'access':
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
+ }
+
+ $native_code = odbc_error($this->connection);
+
+ // S1000 is for "General Error." Let's be more specific.
+ if ($native_code == 'S1000') {
+ $errormsg = odbc_errormsg($this->connection);
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/includes related records.$/i' => DB_ERROR_CONSTRAINT,
+ '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $this->raiseError($code,
+ null, null, null,
+ $native_code . ' ' . $errormsg);
+ }
+ }
+ $errno = DB_ERROR;
+ } else {
+ $errno = $this->errorCode($native_code);
+ }
+ break;
+ default:
+ $errno = $this->errorCode(odbc_error($this->connection));
+ }
+ }
+ return $this->raiseError($errno, null, null, null,
+ $this->errorNative());
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code and message produced by the last query
+ *
+ * @return string the DBMS' error code and message
+ */
+ function errorNative()
+ {
+ if (!is_resource($this->connection)) {
+ return @odbc_error() . ' ' . @odbc_errormsg();
+ }
+ return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @odbc_exec($this->connection, "SELECT * FROM $result");
+ if (!$id) {
+ return $this->odbcRaiseError();
+ }
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @odbc_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $col = $i + 1;
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@odbc_field_name($id, $col)),
+ 'type' => @odbc_field_type($id, $col),
+ 'len' => @odbc_field_len($id, $col),
+ 'flags' => '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @odbc_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the list of objects requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ * @since Method available since Release 1.7.0
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'databases':
+ if (!function_exists('odbc_data_source')) {
+ return null;
+ }
+ $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST);
+ if (is_array($res)) {
+ $out = array($res['server']);
+ while($res = @odbc_data_source($this->connection,
+ SQL_FETCH_NEXT))
+ {
+ $out[] = $res['server'];
+ }
+ return $out;
+ } else {
+ return $this->odbcRaiseError();
+ }
+ break;
+ case 'tables':
+ case 'schema.tables':
+ $keep = 'TABLE';
+ break;
+ case 'views':
+ $keep = 'VIEW';
+ break;
+ default:
+ return null;
+ }
+
+ /*
+ * Removing non-conforming items in the while loop rather than
+ * in the odbc_tables() call because some backends choke on this:
+ * odbc_tables($this->connection, '', '', '', 'TABLE')
+ */
+ $res = @odbc_tables($this->connection);
+ if (!$res) {
+ return $this->odbcRaiseError();
+ }
+ $out = array();
+ while ($row = odbc_fetch_array($res)) {
+ if ($row['TABLE_TYPE'] != $keep) {
+ continue;
+ }
+ if ($type == 'schema.tables') {
+ $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME'];
+ } else {
+ $out[] = $row['TABLE_NAME'];
+ }
+ }
+ return $out;
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's pgsql extension
+ * for interacting with PostgreSQL databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Rui Hirokawa <hirokawa@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: pgsql.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's pgsql extension
+ * for interacting with PostgreSQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Rui Hirokawa <hirokawa@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_pgsql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'pgsql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'pgsql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '4.3.0',
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => true,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ */
+ var $affected = 0;
+
+ /**
+ * The current row being looked at in fetchInto()
+ * @var array
+ * @access private
+ */
+ var $row = array();
+
+ /**
+ * The number of rows in a given result set
+ * @var array
+ * @access private
+ */
+ var $_num_rows = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_pgsql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's pgsql driver supports the following extra DSN options:
+ * + connect_timeout How many seconds to wait for a connection to
+ * be established. Available since PEAR DB 1.7.0.
+ * + new_link If set to true, causes subsequent calls to
+ * connect() to return a new connection link
+ * instead of the existing one. WARNING: this is
+ * not portable to other DBMS's. Available only
+ * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0.
+ * + options Command line options to be sent to the server.
+ * Available since PEAR DB 1.6.4.
+ * + service Specifies a service name in pg_service.conf that
+ * holds additional connection parameters.
+ * Available since PEAR DB 1.7.0.
+ * + sslmode How should SSL be used when connecting? Values:
+ * disable, allow, prefer or require.
+ * Available since PEAR DB 1.7.0.
+ * + tty This was used to specify where to send server
+ * debug output. Available since PEAR DB 1.6.4.
+ *
+ * Example of connecting to a new link via a socket:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('pgsql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp';
+
+ $params = array('');
+ if ($protocol == 'tcp') {
+ if ($dsn['hostspec']) {
+ $params[0] .= 'host=' . $dsn['hostspec'];
+ }
+ if ($dsn['port']) {
+ $params[0] .= ' port=' . $dsn['port'];
+ }
+ } elseif ($protocol == 'unix') {
+ // Allow for pg socket in non-standard locations.
+ if ($dsn['socket']) {
+ $params[0] .= 'host=' . $dsn['socket'];
+ }
+ if ($dsn['port']) {
+ $params[0] .= ' port=' . $dsn['port'];
+ }
+ }
+ if ($dsn['database']) {
+ $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\'';
+ }
+ if ($dsn['username']) {
+ $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\'';
+ }
+ if ($dsn['password']) {
+ $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\'';
+ }
+ if (!empty($dsn['options'])) {
+ $params[0] .= ' options=' . $dsn['options'];
+ }
+ if (!empty($dsn['tty'])) {
+ $params[0] .= ' tty=' . $dsn['tty'];
+ }
+ if (!empty($dsn['connect_timeout'])) {
+ $params[0] .= ' connect_timeout=' . $dsn['connect_timeout'];
+ }
+ if (!empty($dsn['sslmode'])) {
+ $params[0] .= ' sslmode=' . $dsn['sslmode'];
+ }
+ if (!empty($dsn['service'])) {
+ $params[0] .= ' service=' . $dsn['service'];
+ }
+
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ if (version_compare(phpversion(), '4.3.0', '>=')) {
+ $params[] = PGSQL_CONNECT_FORCE_NEW;
+ }
+ }
+
+ $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @pg_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @pg_exec($this->connection, 'begin;');
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @pg_exec($this->connection, $query);
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if ($ismanip) {
+ $this->affected = @pg_affected_rows($result);
+ return DB_OK;
+ } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|SHOW)\s/si', $query)) {
+ /* PostgreSQL commands:
+ ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
+ CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
+ GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
+ REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
+ UNLISTEN, UPDATE, VACUUM
+ */
+ $this->row[(int)$result] = 0; // reset the row counter.
+ $numrows = $this->numRows($result);
+ if (is_object($numrows)) {
+ return $numrows;
+ }
+ $this->_num_rows[(int)$result] = $numrows;
+ $this->affected = 0;
+ return $result;
+ } else {
+ $this->affected = 0;
+ return DB_OK;
+ }
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal pgsql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ $result_int = (int)$result;
+ $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
+ if ($rownum >= $this->_num_rows[$result_int]) {
+ return null;
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @pg_fetch_row($result, $rownum);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ $this->row[$result_int] = ++$rownum;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ if (is_resource($result)) {
+ unset($this->row[(int)$result]);
+ unset($this->_num_rows[(int)$result]);
+ $this->affected = 0;
+ return @pg_freeresult($result);
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ * @internal
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * + null = the string <samp>NULL</samp>
+ * + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
+ * + integer or double = the unquoted number
+ * + other (including strings and numeric strings) =
+ * the data escaped according to MySQL's settings
+ * then encapsulated between single quotes
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in) || is_double($in)) {
+ return $in;
+ } elseif (is_bool($in)) {
+ return $in ? 'TRUE' : 'FALSE';
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * {@internal PostgreSQL treats a backslash as an escape character,
+ * so they are escaped as well.
+ *
+ * Not using pg_escape_string() yet because it requires PostgreSQL
+ * to be at version 7.2 or greater.}}
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return str_replace("'", "''", str_replace('\\', '\\\\', $str));
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @pg_numfields($result);
+ if (!$cols) {
+ return $this->pgsqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @pg_numrows($result);
+ if ($rows === null) {
+ return $this->pgsqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ // (disabled) hack to shut up error messages from libpq.a
+ //@fclose(@fopen("php://stderr", "w"));
+ $result = @pg_exec($this->connection, 'end;');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @pg_exec($this->connection, 'abort;');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return $this->affected;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_pgsql::createSequence(), DB_pgsql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = false;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result =& $this->query("SELECT NEXTVAL('${seqname}')");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = true;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_pgsql::nextID(), DB_pgsql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $result = $this->query("CREATE SEQUENCE ${seqname}");
+ return $result;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_pgsql::nextID(), DB_pgsql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return "$query LIMIT $count OFFSET $from";
+ }
+
+ // }}}
+ // {{{ pgsqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_pgsql::errorNative(), DB_pgsql::errorCode()
+ */
+ function pgsqlRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * {@internal Error messages are used instead of error codes
+ * in order to support older versions of PostgreSQL.}}
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @pg_errormessage($this->connection);
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message.
+ *
+ * @param string $errormsg error message returned from the database
+ * @return integer an error number from a DB error constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/(relation|sequence|table).*does not exist|class .* not found/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/index .* does not exist/'
+ => DB_ERROR_NOT_FOUND,
+ '/column .* does not exist/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/relation .* already exists/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/(divide|division) by zero$/i'
+ => DB_ERROR_DIVZERO,
+ '/pg_atoi: error in .*: can\'t parse /i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/invalid input syntax for( type)? (integer|numeric)/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/value .* is out of range for type \w*int/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/integer out of range/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/value too long for type character/i'
+ => DB_ERROR_INVALID,
+ '/attribute .* not found|relation .* does not have attribute/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/column .* specified in USING clause does not exist in (left|right) table/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/parser: parse error at or near/i'
+ => DB_ERROR_SYNTAX,
+ '/syntax error at/'
+ => DB_ERROR_SYNTAX,
+ '/column reference .* is ambiguous/i'
+ => DB_ERROR_SYNTAX,
+ '/permission denied/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/violates not-null constraint/'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/violates [\w ]+ constraint/'
+ => DB_ERROR_CONSTRAINT,
+ '/referential integrity violation/'
+ => DB_ERROR_CONSTRAINT,
+ '/more expressions than target columns/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @pg_numfields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@pg_fieldname($id, $i)),
+ 'type' => @pg_fieldtype($id, $i),
+ 'len' => @pg_fieldsize($id, $i),
+ 'flags' => $got_string
+ ? $this->_pgFieldFlags($id, $i, $result)
+ : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @pg_freeresult($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _pgFieldFlags()
+
+ /**
+ * Get a column's flags
+ *
+ * Supports "not_null", "default_value", "primary_key", "unique_key"
+ * and "multiple_key". The default value is passed through
+ * rawurlencode() in case there are spaces in it.
+ *
+ * @param int $resource the PostgreSQL result identifier
+ * @param int $num_field the field number
+ *
+ * @return string the flags
+ *
+ * @access private
+ */
+ function _pgFieldFlags($resource, $num_field, $table_name)
+ {
+ $field_name = @pg_fieldname($resource, $num_field);
+
+ $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
+ FROM pg_attribute f, pg_class tab, pg_type typ
+ WHERE tab.relname = typ.typname
+ AND typ.typrelid = f.attrelid
+ AND f.attname = '$field_name'
+ AND tab.relname = '$table_name'");
+ if (@pg_numrows($result) > 0) {
+ $row = @pg_fetch_row($result, 0);
+ $flags = ($row[0] == 't') ? 'not_null ' : '';
+
+ if ($row[1] == 't') {
+ $result = @pg_exec($this->connection, "SELECT a.adsrc
+ FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
+ WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
+ AND f.attrelid = a.adrelid AND f.attname = '$field_name'
+ AND tab.relname = '$table_name' AND f.attnum = a.adnum");
+ $row = @pg_fetch_row($result, 0);
+ $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
+ $flags .= 'default_' . rawurlencode($num) . ' ';
+ }
+ } else {
+ $flags = '';
+ }
+ $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
+ FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
+ WHERE tab.relname = typ.typname
+ AND typ.typrelid = f.attrelid
+ AND f.attrelid = i.indrelid
+ AND f.attname = '$field_name'
+ AND tab.relname = '$table_name'");
+ $count = @pg_numrows($result);
+
+ for ($i = 0; $i < $count ; $i++) {
+ $row = @pg_fetch_row($result, $i);
+ $keys = explode(' ', $row[2]);
+
+ if (in_array($num_field + 1, $keys)) {
+ $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
+ $flags .= ($row[1] == 't') ? 'primary_key ' : '';
+ if (count($keys) > 1)
+ $flags .= 'multiple_key ';
+ }
+ }
+
+ return trim($flags);
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT c.relname AS "Name"'
+ . ' FROM pg_class c, pg_user u'
+ . ' WHERE c.relowner = u.usesysid'
+ . " AND c.relkind = 'r'"
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_views'
+ . ' WHERE viewname = c.relname)'
+ . " AND c.relname !~ '^(pg_|sql_)'"
+ . ' UNION'
+ . ' SELECT c.relname AS "Name"'
+ . ' FROM pg_class c'
+ . " WHERE c.relkind = 'r'"
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_views'
+ . ' WHERE viewname = c.relname)'
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_user'
+ . ' WHERE usesysid = c.relowner)'
+ . " AND c.relname !~ '^pg_'";
+ case 'schema.tables':
+ return "SELECT schemaname || '.' || tablename"
+ . ' AS "Name"'
+ . ' FROM pg_catalog.pg_tables'
+ . ' WHERE schemaname NOT IN'
+ . " ('pg_catalog', 'information_schema', 'pg_toast')";
+ case 'views':
+ // Table cols: viewname | viewowner | definition
+ return 'SELECT viewname from pg_views WHERE schemaname'
+ . " NOT IN ('information_schema', 'pg_catalog')";
+ case 'users':
+ // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil
+ return 'SELECT usename FROM pg_user';
+ case 'databases':
+ return 'SELECT datname FROM pg_database';
+ case 'functions':
+ case 'procedures':
+ return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's sqlite extension
+ * for interacting with SQLite databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Urs Gehrig <urs@circle.ch>
+ * @author Mika Tuupola <tuupola@appelsiini.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
+ * @version CVS: $Id: sqlite.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's sqlite extension
+ * for interacting with SQLite databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * NOTICE: This driver needs PHP's track_errors ini setting to be on.
+ * It is automatically turned on when connecting to the database.
+ * Make sure your scripts don't turn it off.
+ *
+ * @category Database
+ * @package DB
+ * @author Urs Gehrig <urs@circle.ch>
+ * @author Mika Tuupola <tuupola@appelsiini.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_sqlite extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'sqlite';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'sqlite';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => false,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ *
+ * {@internal Error codes according to sqlite_exec. See the online
+ * manual at http://sqlite.org/c_interface.html for info.
+ * This error handling based on sqlite_exec is not yet implemented.}}
+ *
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * SQLite data types
+ *
+ * @link http://www.sqlite.org/datatypes.html
+ *
+ * @var array
+ */
+ var $keywords = array (
+ 'BLOB' => '',
+ 'BOOLEAN' => '',
+ 'CHARACTER' => '',
+ 'CLOB' => '',
+ 'FLOAT' => '',
+ 'INTEGER' => '',
+ 'KEY' => '',
+ 'NATIONAL' => '',
+ 'NUMERIC' => '',
+ 'NVARCHAR' => '',
+ 'PRIMARY' => '',
+ 'TEXT' => '',
+ 'TIMESTAMP' => '',
+ 'UNIQUE' => '',
+ 'VARCHAR' => '',
+ 'VARYING' => '',
+ );
+
+ /**
+ * The most recent error message from $php_errormsg
+ * @var string
+ * @access private
+ */
+ var $_lasterror = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_sqlite()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's sqlite driver supports the following extra DSN options:
+ * + mode The permissions for the database file, in four digit
+ * chmod octal format (eg "0600").
+ *
+ * Example of connecting to a database in read-only mode:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400';
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('sqlite')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ if ($dsn['database']) {
+ if (!file_exists($dsn['database'])) {
+ if (!touch($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+ }
+ if (!isset($dsn['mode']) ||
+ !is_numeric($dsn['mode']))
+ {
+ $mode = 0644;
+ } else {
+ $mode = octdec($dsn['mode']);
+ }
+ if (!chmod($dsn['database'], $mode)) {
+ return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+ }
+ if (!file_exists($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+ }
+ }
+ if (!is_file($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_INVALID);
+ }
+ if (!is_readable($dsn['database'])) {
+ return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+ } else {
+ return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+
+ $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open';
+
+ // track_errors must remain on for simpleQuery()
+ ini_set('track_errors', 1);
+ $php_errormsg = '';
+
+ if (!$this->connection = @$connect_function($dsn['database'])) {
+ return $this->raiseError(DB_ERROR_NODBSELECTED,
+ null, null, null,
+ $php_errormsg);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @sqlite_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * NOTICE: This method needs PHP's track_errors ini setting to be on.
+ * It is automatically turned on when connecting to the database.
+ * Make sure your scripts don't turn it off.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+
+ $php_errormsg = '';
+
+ $result = @sqlite_query($query, $this->connection);
+ $this->_lasterror = $php_errormsg ? $php_errormsg : '';
+
+ $this->result = $result;
+ if (!$this->result) {
+ return $this->sqliteRaiseError(null);
+ }
+
+ // sqlite_query() seems to allways return a resource
+ // so cant use that. Using $ismanip instead
+ if (!$ismanip) {
+ $numRows = $this->numRows($result);
+ if (is_object($numRows)) {
+ // we've got PEAR_Error
+ return $numRows;
+ }
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal sqlite result pointer to the next available result
+ *
+ * @param resource $result the valid sqlite result resource
+ *
+ * @return bool true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@sqlite_seek($this->result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @sqlite_fetch_array($result, SQLITE_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @sqlite_fetch_array($result, SQLITE_NUM);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult(&$result)
+ {
+ // XXX No native free?
+ if (!is_resource($result)) {
+ return false;
+ }
+ $result = null;
+ return true;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @sqlite_num_fields($result);
+ if (!$cols) {
+ return $this->sqliteRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @sqlite_num_rows($result);
+ if ($rows === null) {
+ return $this->sqliteRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affected()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return @sqlite_changes($this->connection);
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_sqlite::nextID(), DB_sqlite::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_sqlite::nextID(), DB_sqlite::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $query = 'CREATE TABLE ' . $seqname .
+ ' (id INTEGER UNSIGNED PRIMARY KEY) ';
+ $result = $this->query($query);
+ if (DB::isError($result)) {
+ return($result);
+ }
+ $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname
+ BEGIN
+ DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID();
+ END ";
+ $result = $this->query($query);
+ if (DB::isError($result)) {
+ return($result);
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_sqlite::createSequence(), DB_sqlite::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)");
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ $id = @sqlite_last_insert_rowid($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ $repeat = 1;
+ }
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ // }}}
+ // {{{ getDbFileStats()
+
+ /**
+ * Get the file stats for the current database
+ *
+ * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size,
+ * atime, mtime, ctime, blksize, blocks or a numeric key between
+ * 0 and 12.
+ *
+ * @param string $arg the array key for stats()
+ *
+ * @return mixed an array on an unspecified key, integer on a passed
+ * arg and false at a stats error
+ */
+ function getDbFileStats($arg = '')
+ {
+ $stats = stat($this->dsn['database']);
+ if ($stats == false) {
+ return false;
+ }
+ if (is_array($stats)) {
+ if (is_numeric($arg)) {
+ if (((int)$arg <= 12) & ((int)$arg >= 0)) {
+ return false;
+ }
+ return $stats[$arg ];
+ }
+ if (array_key_exists(trim($arg), $stats)) {
+ return $stats[$arg ];
+ }
+ }
+ return $stats;
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * In SQLite, this makes things safe for inserts/updates, but may
+ * cause problems when performing text comparisons against columns
+ * containing binary data. See the
+ * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @since Method available since Release 1.6.1
+ * @see DB_common::escapeSimple()
+ */
+ function escapeSimple($str)
+ {
+ return @sqlite_escape_string($str);
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return "$query LIMIT $count OFFSET $from";
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * This little hack lets you know how many rows were deleted
+ * when running a "DELETE FROM table" query. Only implemented
+ * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_common::setOption()
+ */
+ function modifyQuery($query)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
+ if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+ $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+ 'DELETE FROM \1 WHERE 1=1', $query);
+ }
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ sqliteRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_sqlite::errorNative(), DB_sqlite::errorCode()
+ */
+ function sqliteRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+
+ $errorcode = @sqlite_last_error($this->connection);
+ $userinfo = "$errorcode ** $this->last_query";
+
+ return $this->raiseError($errno, null, null, $userinfo, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * {@internal This is used to retrieve more meaningfull error messages
+ * because sqlite_last_error() does not provide adequate info.}}
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return $this->_lasterror;
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message
+ *
+ * @param string $errormsg the error message returned from the database
+ *
+ * @return integer the DB error number
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/^no such table:/' => DB_ERROR_NOSUCHTABLE,
+ '/^no such index:/' => DB_ERROR_NOT_FOUND,
+ '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS,
+ '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT,
+ '/is not unique/' => DB_ERROR_CONSTRAINT,
+ '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT,
+ '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT,
+ '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/^no such column:/' => DB_ERROR_NOSUCHFIELD,
+ '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD,
+ '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX,
+ '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table
+ *
+ * @param string $result a string containing the name of a table
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @sqlite_array_query($this->connection,
+ "PRAGMA table_info('$result');",
+ SQLITE_ASSOC);
+ $got_string = true;
+ } else {
+ $this->last_query = '';
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null,
+ 'This DBMS can not obtain tableInfo' .
+ ' from result sets');
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = count($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ if (strpos($id[$i]['type'], '(') !== false) {
+ $bits = explode('(', $id[$i]['type']);
+ $type = $bits[0];
+ $len = rtrim($bits[1],')');
+ } else {
+ $type = $id[$i]['type'];
+ $len = 0;
+ }
+
+ $flags = '';
+ if ($id[$i]['pk']) {
+ $flags .= 'primary_key ';
+ }
+ if ($id[$i]['notnull']) {
+ $flags .= 'not_null ';
+ }
+ if ($id[$i]['dflt_value'] !== null) {
+ $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']);
+ }
+ $flags = trim($flags);
+
+ $res[$i] = array(
+ 'table' => $case_func($result),
+ 'name' => $case_func($id[$i]['name']),
+ 'type' => $type,
+ 'len' => $len,
+ 'flags' => $flags,
+ );
+
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ * @param array $args SQLITE DRIVER ONLY: a private array of arguments
+ * used by the getSpecialQuery(). Do not use
+ * this directly.
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type, $args = array())
+ {
+ if (!is_array($args)) {
+ return $this->raiseError('no key specified', null, null, null,
+ 'Argument has to be an array.');
+ }
+
+ switch ($type) {
+ case 'master':
+ return 'SELECT * FROM sqlite_master;';
+ case 'tables':
+ return "SELECT name FROM sqlite_master WHERE type='table' "
+ . 'UNION ALL SELECT name FROM sqlite_temp_master '
+ . "WHERE type='table' ORDER BY name;";
+ case 'schema':
+ return 'SELECT sql FROM (SELECT * FROM sqlite_master '
+ . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+ . "WHERE type!='meta' "
+ . 'ORDER BY tbl_name, type DESC, name;';
+ case 'schemax':
+ case 'schema_x':
+ /*
+ * Use like:
+ * $res = $db->query($db->getSpecialQuery('schema_x',
+ * array('table' => 'table3')));
+ */
+ return 'SELECT sql FROM (SELECT * FROM sqlite_master '
+ . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+ . "WHERE tbl_name LIKE '{$args['table']}' "
+ . "AND type!='meta' "
+ . 'ORDER BY type DESC, name;';
+ case 'alter':
+ /*
+ * SQLite does not support ALTER TABLE; this is a helper query
+ * to handle this. 'table' represents the table name, 'rows'
+ * the news rows to create, 'save' the row(s) to keep _with_
+ * the data.
+ *
+ * Use like:
+ * $args = array(
+ * 'table' => $table,
+ * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT",
+ * 'save' => "NULL, titel, content, datetime"
+ * );
+ * $res = $db->query( $db->getSpecialQuery('alter', $args));
+ */
+ $rows = strtr($args['rows'], $this->keywords);
+
+ $q = array(
+ 'BEGIN TRANSACTION',
+ "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})",
+ "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}",
+ "DROP TABLE {$args['table']}",
+ "CREATE TABLE {$args['table']} ({$args['rows']})",
+ "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup",
+ "DROP TABLE {$args['table']}_backup",
+ 'COMMIT',
+ );
+
+ /*
+ * This is a dirty hack, since the above query will not get
+ * executed with a single query call so here the query method
+ * will be called directly and return a select instead.
+ */
+ foreach ($q as $query) {
+ $this->query($query);
+ }
+ return "SELECT * FROM {$args['table']};";
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Provides an object interface to a table row
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <stig@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: storage.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB class so it can be extended from
+ */
+require_once 'DB.php';
+
+/**
+ * Provides an object interface to a table row
+ *
+ * It lets you add, delete and change rows using objects rather than SQL
+ * statements.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <stig@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_storage extends PEAR
+{
+ // {{{ properties
+
+ /** the name of the table (or view, if the backend database supports
+ updates in views) we hold data from */
+ var $_table = null;
+
+ /** which column(s) in the table contains primary keys, can be a
+ string for single-column primary keys, or an array of strings
+ for multiple-column primary keys */
+ var $_keycolumn = null;
+
+ /** DB connection handle used for all transactions */
+ var $_dbh = null;
+
+ /** an assoc with the names of database fields stored as properties
+ in this object */
+ var $_properties = array();
+
+ /** an assoc with the names of the properties in this object that
+ have been changed since they were fetched from the database */
+ var $_changes = array();
+
+ /** flag that decides if data in this object can be changed.
+ objects that don't have their table's key column in their
+ property lists will be flagged as read-only. */
+ var $_readonly = false;
+
+ /** function or method that implements a validator for fields that
+ are set, this validator function returns true if the field is
+ valid, false if not */
+ var $_validator = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Constructor
+ *
+ * @param $table string the name of the database table
+ *
+ * @param $keycolumn mixed string with name of key column, or array of
+ * strings if the table has a primary key of more than one column
+ *
+ * @param $dbh object database connection object
+ *
+ * @param $validator mixed function or method used to validate
+ * each new value, called with three parameters: the name of the
+ * field/column that is changing, a reference to the new value and
+ * a reference to this object
+ *
+ */
+ function DB_storage($table, $keycolumn, &$dbh, $validator = null)
+ {
+ $this->PEAR('DB_Error');
+ $this->_table = $table;
+ $this->_keycolumn = $keycolumn;
+ $this->_dbh = $dbh;
+ $this->_readonly = false;
+ $this->_validator = $validator;
+ }
+
+ // }}}
+ // {{{ _makeWhere()
+
+ /**
+ * Utility method to build a "WHERE" clause to locate ourselves in
+ * the table.
+ *
+ * XXX future improvement: use rowids?
+ *
+ * @access private
+ */
+ function _makeWhere($keyval = null)
+ {
+ if (is_array($this->_keycolumn)) {
+ if ($keyval === null) {
+ for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
+ $keyval[] = $this->{$this->_keycolumn[$i]};
+ }
+ }
+ $whereclause = '';
+ for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
+ if ($i > 0) {
+ $whereclause .= ' AND ';
+ }
+ $whereclause .= $this->_keycolumn[$i];
+ if (is_null($keyval[$i])) {
+ // there's not much point in having a NULL key,
+ // but we support it anyway
+ $whereclause .= ' IS NULL';
+ } else {
+ $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
+ }
+ }
+ } else {
+ if ($keyval === null) {
+ $keyval = @$this->{$this->_keycolumn};
+ }
+ $whereclause = $this->_keycolumn;
+ if (is_null($keyval)) {
+ // there's not much point in having a NULL key,
+ // but we support it anyway
+ $whereclause .= ' IS NULL';
+ } else {
+ $whereclause .= ' = ' . $this->_dbh->quote($keyval);
+ }
+ }
+ return $whereclause;
+ }
+
+ // }}}
+ // {{{ setup()
+
+ /**
+ * Method used to initialize a DB_storage object from the
+ * configured table.
+ *
+ * @param $keyval mixed the key[s] of the row to fetch (string or array)
+ *
+ * @return int DB_OK on success, a DB error if not
+ */
+ function setup($keyval)
+ {
+ $whereclause = $this->_makeWhere($keyval);
+ $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
+ $sth = $this->_dbh->query($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
+ if (DB::isError($row)) {
+ return $row;
+ }
+ if (!$row) {
+ return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null,
+ $query, null, true);
+ }
+ foreach ($row as $key => $value) {
+ $this->_properties[$key] = true;
+ $this->$key = $value;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ insert()
+
+ /**
+ * Create a new (empty) row in the configured table for this
+ * object.
+ */
+ function insert($newpk)
+ {
+ if (is_array($this->_keycolumn)) {
+ $primarykey = $this->_keycolumn;
+ } else {
+ $primarykey = array($this->_keycolumn);
+ }
+ settype($newpk, "array");
+ for ($i = 0; $i < sizeof($primarykey); $i++) {
+ $pkvals[] = $this->_dbh->quote($newpk[$i]);
+ }
+
+ $sth = $this->_dbh->query("INSERT INTO $this->_table (" .
+ implode(",", $primarykey) . ") VALUES(" .
+ implode(",", $pkvals) . ")");
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ if (sizeof($newpk) == 1) {
+ $newpk = $newpk[0];
+ }
+ $this->setup($newpk);
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Output a simple description of this DB_storage object.
+ * @return string object description
+ */
+ function toString()
+ {
+ $info = strtolower(get_class($this));
+ $info .= " (table=";
+ $info .= $this->_table;
+ $info .= ", keycolumn=";
+ if (is_array($this->_keycolumn)) {
+ $info .= "(" . implode(",", $this->_keycolumn) . ")";
+ } else {
+ $info .= $this->_keycolumn;
+ }
+ $info .= ", dbh=";
+ if (is_object($this->_dbh)) {
+ $info .= $this->_dbh->toString();
+ } else {
+ $info .= "null";
+ }
+ $info .= ")";
+ if (sizeof($this->_properties)) {
+ $info .= " [loaded, key=";
+ $keyname = $this->_keycolumn;
+ if (is_array($keyname)) {
+ $info .= "(";
+ for ($i = 0; $i < sizeof($keyname); $i++) {
+ if ($i > 0) {
+ $info .= ",";
+ }
+ $info .= $this->$keyname[$i];
+ }
+ $info .= ")";
+ } else {
+ $info .= $this->$keyname;
+ }
+ $info .= "]";
+ }
+ if (sizeof($this->_changes)) {
+ $info .= " [modified]";
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ dump()
+
+ /**
+ * Dump the contents of this object to "standard output".
+ */
+ function dump()
+ {
+ foreach ($this->_properties as $prop => $foo) {
+ print "$prop = ";
+ print htmlentities($this->$prop);
+ print "<br />\n";
+ }
+ }
+
+ // }}}
+ // {{{ &create()
+
+ /**
+ * Static method used to create new DB storage objects.
+ * @param $data assoc. array where the keys are the names
+ * of properties/columns
+ * @return object a new instance of DB_storage or a subclass of it
+ */
+ function &create($table, &$data)
+ {
+ $classname = strtolower(get_class($this));
+ $obj =& new $classname($table);
+ foreach ($data as $name => $value) {
+ $obj->_properties[$name] = true;
+ $obj->$name = &$value;
+ }
+ return $obj;
+ }
+
+ // }}}
+ // {{{ loadFromQuery()
+
+ /**
+ * Loads data into this object from the given query. If this
+ * object already contains table data, changes will be saved and
+ * the object re-initialized first.
+ *
+ * @param $query SQL query
+ *
+ * @param $params parameter list in case you want to use
+ * prepare/execute mode
+ *
+ * @return int DB_OK on success, DB_WARNING_READ_ONLY if the
+ * returned object is read-only (because the object's specified
+ * key column was not found among the columns returned by $query),
+ * or another DB error code in case of errors.
+ */
+// XXX commented out for now
+/*
+ function loadFromQuery($query, $params = null)
+ {
+ if (sizeof($this->_properties)) {
+ if (sizeof($this->_changes)) {
+ $this->store();
+ $this->_changes = array();
+ }
+ $this->_properties = array();
+ }
+ $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
+ if (DB::isError($rowdata)) {
+ return $rowdata;
+ }
+ reset($rowdata);
+ $found_keycolumn = false;
+ while (list($key, $value) = each($rowdata)) {
+ if ($key == $this->_keycolumn) {
+ $found_keycolumn = true;
+ }
+ $this->_properties[$key] = true;
+ $this->$key = &$value;
+ unset($value); // have to unset, or all properties will
+ // refer to the same value
+ }
+ if (!$found_keycolumn) {
+ $this->_readonly = true;
+ return DB_WARNING_READ_ONLY;
+ }
+ return DB_OK;
+ }
+ */
+
+ // }}}
+ // {{{ set()
+
+ /**
+ * Modify an attriute value.
+ */
+ function set($property, $newvalue)
+ {
+ // only change if $property is known and object is not
+ // read-only
+ if ($this->_readonly) {
+ return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
+ null, null, null, true);
+ }
+ if (@isset($this->_properties[$property])) {
+ if (empty($this->_validator)) {
+ $valid = true;
+ } else {
+ $valid = @call_user_func($this->_validator,
+ $this->_table,
+ $property,
+ $newvalue,
+ $this->$property,
+ $this);
+ }
+ if ($valid) {
+ $this->$property = $newvalue;
+ if (empty($this->_changes[$property])) {
+ $this->_changes[$property] = 0;
+ } else {
+ $this->_changes[$property]++;
+ }
+ } else {
+ return $this->raiseError(null, DB_ERROR_INVALID, null,
+ null, "invalid field: $property",
+ null, true);
+ }
+ return true;
+ }
+ return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null,
+ null, "unknown field: $property",
+ null, true);
+ }
+
+ // }}}
+ // {{{ &get()
+
+ /**
+ * Fetch an attribute value.
+ *
+ * @param string attribute name
+ *
+ * @return attribute contents, or null if the attribute name is
+ * unknown
+ */
+ function &get($property)
+ {
+ // only return if $property is known
+ if (isset($this->_properties[$property])) {
+ return $this->$property;
+ }
+ $tmp = null;
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ _DB_storage()
+
+ /**
+ * Destructor, calls DB_storage::store() if there are changes
+ * that are to be kept.
+ */
+ function _DB_storage()
+ {
+ if (sizeof($this->_changes)) {
+ $this->store();
+ }
+ $this->_properties = array();
+ $this->_changes = array();
+ $this->_table = null;
+ }
+
+ // }}}
+ // {{{ store()
+
+ /**
+ * Stores changes to this object in the database.
+ *
+ * @return DB_OK or a DB error
+ */
+ function store()
+ {
+ foreach ($this->_changes as $name => $foo) {
+ $params[] = &$this->$name;
+ $vars[] = $name . ' = ?';
+ }
+ if ($vars) {
+ $query = 'UPDATE ' . $this->_table . ' SET ' .
+ implode(', ', $vars) . ' WHERE ' .
+ $this->_makeWhere();
+ $stmt = $this->_dbh->prepare($query);
+ $res = $this->_dbh->execute($stmt, $params);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ $this->_changes = array();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove the row represented by this object from the database.
+ *
+ * @return mixed DB_OK or a DB error
+ */
+ function remove()
+ {
+ if ($this->_readonly) {
+ return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
+ null, null, null, true);
+ }
+ $query = 'DELETE FROM ' . $this->_table .' WHERE '.
+ $this->_makeWhere();
+ $res = $this->_dbh->query($query);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ foreach ($this->_properties as $prop => $foo) {
+ unset($this->$prop);
+ }
+ $this->_properties = array();
+ $this->_changes = array();
+ return DB_OK;
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's sybase extension
+ * for interacting with Sybase databases
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Antônio Carlos Venâncio Júnior <floripa@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: sybase.php 12 2005-10-02 11:36:35Z sparc $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's sybase extension
+ * for interacting with Sybase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * WARNING: This driver may fail with multiple connections under the
+ * same user/pass/host and different databases.
+ *
+ * @category Database
+ * @package DB
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Antônio Carlos Venâncio Júnior <floripa@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_sybase extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'sybase';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'sybase';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'emulate',
+ 'new_link' => false,
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_sybase()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's sybase driver supports the following extra DSN options:
+ * + appname The application name to use on this connection.
+ * Available since PEAR DB 1.7.0.
+ * + charset The character set to use on this connection.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('sybase') &&
+ !PEAR::loadExtension('sybase_ct'))
+ {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost';
+ $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false;
+ $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false;
+ $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false;
+
+ $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect';
+
+ if ($dsn['username']) {
+ $this->connection = @$connect_function($dsn['hostspec'],
+ $dsn['username'],
+ $dsn['password'],
+ $dsn['charset'],
+ $dsn['appname']);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ 'The DSN did not contain a username.');
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ @sybase_get_last_message());
+ }
+
+ if ($dsn['database']) {
+ if (!@sybase_select_db($dsn['database'], $this->connection)) {
+ return $this->raiseError(DB_ERROR_NODBSELECTED,
+ null, null, null,
+ @sybase_get_last_message());
+ }
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @sybase_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ if (!@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @sybase_query('BEGIN TRANSACTION', $this->connection);
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @sybase_query($query, $this->connection);
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ if (is_resource($result)) {
+ return $result;
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ return $ismanip ? DB_OK : $result;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal sybase result pointer to the next available result
+ *
+ * @param a valid sybase result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@sybase_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ if (function_exists('sybase_fetch_assoc')) {
+ $arr = @sybase_fetch_assoc($result);
+ } else {
+ if ($arr = @sybase_fetch_array($result)) {
+ foreach ($arr as $key => $value) {
+ if (is_int($key)) {
+ unset($arr[$key]);
+ }
+ }
+ }
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @sybase_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @sybase_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @sybase_num_fields($result);
+ if (!$cols) {
+ return $this->sybaseRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @sybase_num_rows($result);
+ if ($rows === false) {
+ return $this->sybaseRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ $result = @sybase_affected_rows($this->connection);
+ } else {
+ $result = 0;
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_sybase::createSequence(), DB_sybase::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ if (!@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $repeat = 0;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
+ {
+ $repeat = 1;
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } elseif (!DB::isError($result)) {
+ $result =& $this->query("SELECT @@IDENTITY FROM $seqname");
+ $repeat = 0;
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ return $result[0];
+ }
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_sybase::nextID(), DB_sybase::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->query('CREATE TABLE '
+ . $this->getSequenceName($seq_name)
+ . ' (id numeric(10, 0) IDENTITY NOT NULL,'
+ . ' vapor int NULL)');
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_sybase::nextID(), DB_sybase::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if (!@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @sybase_query('COMMIT', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if (!@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $result = @sybase_query('ROLLBACK', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->sybaseRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ sybaseRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_sybase::errorNative(), DB_sybase::errorCode()
+ */
+ function sybaseRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @sybase_get_last_message();
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message.
+ *
+ * @param string $errormsg error message returned from the database
+ * @return integer an error number from a DB error constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/Incorrect syntax near/'
+ => DB_ERROR_SYNTAX,
+ '/^Unclosed quote before the character string [\"\'].*[\"\']\./'
+ => DB_ERROR_SYNTAX,
+ '/Implicit conversion (from datatype|of NUMERIC value)/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./'
+ => DB_ERROR_NOSUCHTABLE,
+ '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^.+ permission denied on object .+, database .+, owner .+/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/^.* permission denied, database .+, owner .+/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/[^.*] not found\./'
+ => DB_ERROR_NOSUCHTABLE,
+ '/There is already an object named/'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/Invalid column name/'
+ => DB_ERROR_NOSUCHFIELD,
+ '/does not allow null values/'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/Command has been aborted/'
+ => DB_ERROR_CONSTRAINT,
+ '/^Cannot drop the index .* because it doesn\'t exist/i'
+ => DB_ERROR_NOT_FOUND,
+ '/^There is already an index/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/^There are fewer columns in the INSERT statement than values specified/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ * @since Method available since Release 1.6.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ if (!@sybase_select_db($this->_db, $this->connection)) {
+ return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ $id = @sybase_query("SELECT * FROM $result WHERE 1=0",
+ $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @sybase_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $f = @sybase_fetch_field($id, $i);
+ // column_source is often blank
+ $res[$i] = array(
+ 'table' => $got_string
+ ? $case_func($result)
+ : $case_func($f->column_source),
+ 'name' => $case_func($f->name),
+ 'type' => $f->type,
+ 'len' => $f->max_length,
+ 'flags' => '',
+ );
+ if ($res[$i]['table']) {
+ $res[$i]['flags'] = $this->_sybase_field_flags(
+ $res[$i]['table'], $res[$i]['name']);
+ }
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @sybase_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _sybase_field_flags()
+
+ /**
+ * Get the flags for a field
+ *
+ * Currently supports:
+ * + <samp>unique_key</samp> (unique index, unique check or primary_key)
+ * + <samp>multiple_key</samp> (multi-key index)
+ *
+ * @param string $table the table name
+ * @param string $column the field name
+ *
+ * @return string space delimited string of flags. Empty string if none.
+ *
+ * @access private
+ */
+ function _sybase_field_flags($table, $column)
+ {
+ static $tableName = null;
+ static $flags = array();
+
+ if ($table != $tableName) {
+ $flags = array();
+ $tableName = $table;
+
+ // get unique/primary keys
+ $res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC);
+
+ if (!isset($res[0]['index_description'])) {
+ return '';
+ }
+
+ foreach ($res as $val) {
+ $keys = explode(', ', trim($val['index_keys']));
+
+ if (sizeof($keys) > 1) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'multiple_key');
+ }
+ }
+
+ if (strpos($val['index_description'], 'unique')) {
+ foreach ($keys as $key) {
+ $this->_add_flag($flags[$key], 'unique_key');
+ }
+ }
+ }
+
+ }
+
+ if (array_key_exists($column, $flags)) {
+ return(implode(' ', $flags[$column]));
+ }
+
+ return '';
+ }
+
+ // }}}
+ // {{{ _add_flag()
+
+ /**
+ * Adds a string to the flags array if the flag is not yet in there
+ * - if there is no flag present the array is created
+ *
+ * @param array $array reference of flags array to add a value to
+ * @param mixed $value value to add to the flag array
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _add_flag(&$array, $value)
+ {
+ if (!is_array($array)) {
+ $array = array($value);
+ } elseif (!in_array($value, $array)) {
+ array_push($array, $value);
+ }
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return "SELECT name FROM sysobjects WHERE type = 'U'"
+ . ' ORDER BY name';
+ case 'views':
+ return "SELECT name FROM sysobjects WHERE type = 'V'";
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | Copyright (c) 2003-2005 The PHP Group |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@phpguru.org> |
+// | Tomas V.V.Cox <cox@idecnet.com> (port to PEAR) |
+// +-----------------------------------------------------------------------+
+//
+// $Id: mime.php 260 2006-06-09 16:47:21Z afladmark $
+
+require_once('PEAR.php');
+require_once('Mail/mimePart.php');
+
+/**
+ * Mime mail composer class. Can handle: text and html bodies, embedded html
+ * images and attachments.
+ * Documentation and examples of this class are avaible here:
+ * http://pear.php.net/manual/
+ *
+ * @notes This class is based on HTML Mime Mail class from
+ * Richard Heyes <richard@phpguru.org> which was based also
+ * in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it> and
+ * Sascha Schumann <sascha@schumann.cx>
+ *
+ * @author Richard Heyes <richard.heyes@heyes-computing.net>
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @package Mail
+ * @access public
+ */
+class Mail_mime
+{
+ /**
+ * Contains the plain text part of the email
+ * @var string
+ */
+ var $_txtbody;
+ /**
+ * Contains the html part of the email
+ * @var string
+ */
+ var $_htmlbody;
+ /**
+ * contains the mime encoded text
+ * @var string
+ */
+ var $_mime;
+ /**
+ * contains the multipart content
+ * @var string
+ */
+ var $_multipart;
+ /**
+ * list of the attached images
+ * @var array
+ */
+ var $_html_images = array();
+ /**
+ * list of the attachements
+ * @var array
+ */
+ var $_parts = array();
+ /**
+ * Build parameters
+ * @var array
+ */
+ var $_build_params = array();
+ /**
+ * Headers for the mail
+ * @var array
+ */
+ var $_headers = array();
+ /**
+ * End Of Line sequence (for serialize)
+ * @var string
+ */
+ var $_eol;
+
+
+ /**
+ * Constructor function
+ *
+ * @access public
+ */
+ function Mail_mime($crlf = "\r\n")
+ {
+ $this->_setEOL($crlf);
+ $this->_build_params = array(
+ 'head_encoding' => 'quoted-printable',
+ 'text_encoding' => '7bit',
+ 'html_encoding' => 'quoted-printable',
+ '7bit_wrap' => 998,
+ 'html_charset' => 'ISO-8859-1',
+ 'text_charset' => 'ISO-8859-1',
+ 'head_charset' => 'ISO-8859-1'
+ );
+ }
+
+ /**
+ * Wakeup (unserialize) - re-sets EOL constant
+ *
+ * @access private
+ */
+ function __wakeup()
+ {
+ $this->_setEOL($this->_eol);
+ }
+
+ /**
+ * Accessor function to set the body text. Body text is used if
+ * it's not an html mail being sent or else is used to fill the
+ * text/plain part that emails clients who don't support
+ * html should show.
+ *
+ * @param string $data Either a string or
+ * the file name with the contents
+ * @param bool $isfile If true the first param should be treated
+ * as a file name, else as a string (default)
+ * @param bool $append If true the text or file is appended to
+ * the existing body, else the old body is
+ * overwritten
+ * @return mixed true on success or PEAR_Error object
+ * @access public
+ */
+ function setTXTBody($data, $isfile = false, $append = false)
+ {
+ if (!$isfile) {
+ if (!$append) {
+ $this->_txtbody = $data;
+ } else {
+ $this->_txtbody .= $data;
+ }
+ } else {
+ $cont = $this->_file2str($data);
+ if (PEAR::isError($cont)) {
+ return $cont;
+ }
+ if (!$append) {
+ $this->_txtbody = $cont;
+ } else {
+ $this->_txtbody .= $cont;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Adds a html part to the mail
+ *
+ * @param string $data Either a string or the file name with the
+ * contents
+ * @param bool $isfile If true the first param should be treated
+ * as a file name, else as a string (default)
+ * @return mixed true on success or PEAR_Error object
+ * @access public
+ */
+ function setHTMLBody($data, $isfile = false)
+ {
+ if (!$isfile) {
+ $this->_htmlbody = $data;
+ } else {
+ $cont = $this->_file2str($data);
+ if (PEAR::isError($cont)) {
+ return $cont;
+ }
+ $this->_htmlbody = $cont;
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds an image to the list of embedded images.
+ *
+ * @param string $file The image file name OR image data itself
+ * @param string $c_type The content type
+ * @param string $name The filename of the image.
+ * Only use if $file is the image data
+ * @param bool $isfilename Whether $file is a filename or not
+ * Defaults to true
+ * @return mixed true on success or PEAR_Error object
+ * @access public
+ */
+ function addHTMLImage($file, $c_type='application/octet-stream',
+ $name = '', $isfilename = true)
+ {
+ $filedata = ($isfilename === true) ? $this->_file2str($file)
+ : $file;
+ if ($isfilename === true) {
+ $filename = ($name == '' ? $file : $name);
+ } else {
+ $filename = $name;
+ }
+ if (PEAR::isError($filedata)) {
+ return $filedata;
+ }
+ $this->_html_images[] = array(
+ 'body' => $filedata,
+ 'name' => $filename,
+ 'c_type' => $c_type,
+ 'cid' => md5(uniqid(time()))
+ );
+ return true;
+ }
+
+ /**
+ * Adds a file to the list of attachments.
+ *
+ * @param string $file The file name of the file to attach
+ * OR the file contents itself
+ * @param string $c_type The content type
+ * @param string $name The filename of the attachment
+ * Only use if $file is the contents
+ * @param bool $isFilename Whether $file is a filename or not
+ * Defaults to true
+ * @param string $encoding The type of encoding to use.
+ * Defaults to base64.
+ * Possible values: 7bit, 8bit, base64,
+ * or quoted-printable.
+ * @param string $disposition The content-disposition of this file
+ * Defaults to attachment.
+ * Possible values: attachment, inline.
+ * @param string $charset The character set used in the filename
+ * of this attachment.
+ * @return mixed true on success or PEAR_Error object
+ * @access public
+ */
+ function addAttachment($file, $c_type = 'application/octet-stream',
+ $name = '', $isfilename = true,
+ $encoding = 'base64',
+ $disposition = 'attachment', $charset = '')
+ {
+ $filedata = ($isfilename === true) ? $this->_file2str($file)
+ : $file;
+ if ($isfilename === true) {
+ // Force the name the user supplied, otherwise use $file
+ $filename = (!empty($name)) ? $name : $file;
+ } else {
+ $filename = $name;
+ }
+ if (empty($filename)) {
+ $err = PEAR::raiseError(
+ "The supplied filename for the attachment can't be empty"
+ );
+ return $err;
+ }
+ $filename = basename($filename);
+ if (PEAR::isError($filedata)) {
+ return $filedata;
+ }
+
+ $this->_parts[] = array(
+ 'body' => $filedata,
+ 'name' => $filename,
+ 'c_type' => $c_type,
+ 'encoding' => $encoding,
+ 'charset' => $charset,
+ 'disposition' => $disposition
+ );
+ return true;
+ }
+
+ /**
+ * Get the contents of the given file name as string
+ *
+ * @param string $file_name path of file to process
+ * @return string contents of $file_name
+ * @access private
+ */
+ function &_file2str($file_name)
+ {
+ if (!is_readable($file_name)) {
+ $err = PEAR::raiseError('File is not readable ' . $file_name);
+ return $err;
+ }
+ if (!$fd = fopen($file_name, 'rb')) {
+ $err = PEAR::raiseError('Could not open ' . $file_name);
+ return $err;
+ }
+ $filesize = filesize($file_name);
+ if ($filesize == 0){
+ $cont = "";
+ }else{
+ if ($magic_quote_setting = get_magic_quotes_runtime()){
+ set_magic_quotes_runtime(0);
+ }
+ $cont = fread($fd, $filesize);
+ if ($magic_quote_setting){
+ set_magic_quotes_runtime($magic_quote_setting);
+ }
+ }
+ fclose($fd);
+ return $cont;
+ }
+
+ /**
+ * Adds a text subpart to the mimePart object and
+ * returns it during the build process.
+ *
+ * @param mixed The object to add the part to, or
+ * null if a new object is to be created.
+ * @param string The text to add.
+ * @return object The text mimePart object
+ * @access private
+ */
+ function &_addTextPart(&$obj, $text)
+ {
+ $params['content_type'] = 'text/plain';
+ $params['encoding'] = $this->_build_params['text_encoding'];
+ $params['charset'] = $this->_build_params['text_charset'];
+ if (is_object($obj)) {
+ $ret = $obj->addSubpart($text, $params);
+ return $ret;
+ } else {
+ $ret = new Mail_mimePart($text, $params);
+ return $ret;
+ }
+ }
+
+ /**
+ * Adds a html subpart to the mimePart object and
+ * returns it during the build process.
+ *
+ * @param mixed The object to add the part to, or
+ * null if a new object is to be created.
+ * @return object The html mimePart object
+ * @access private
+ */
+ function &_addHtmlPart(&$obj)
+ {
+ $params['content_type'] = 'text/html';
+ $params['encoding'] = $this->_build_params['html_encoding'];
+ $params['charset'] = $this->_build_params['html_charset'];
+ if (is_object($obj)) {
+ $ret = $obj->addSubpart($this->_htmlbody, $params);
+ return $ret;
+ } else {
+ $ret = new Mail_mimePart($this->_htmlbody, $params);
+ return $ret;
+ }
+ }
+
+ /**
+ * Creates a new mimePart object, using multipart/mixed as
+ * the initial content-type and returns it during the
+ * build process.
+ *
+ * @return object The multipart/mixed mimePart object
+ * @access private
+ */
+ function &_addMixedPart()
+ {
+ $params['content_type'] = 'multipart/mixed';
+ $ret = new Mail_mimePart('', $params);
+ return $ret;
+ }
+
+ /**
+ * Adds a multipart/alternative part to a mimePart
+ * object (or creates one), and returns it during
+ * the build process.
+ *
+ * @param mixed The object to add the part to, or
+ * null if a new object is to be created.
+ * @return object The multipart/mixed mimePart object
+ * @access private
+ */
+ function &_addAlternativePart(&$obj)
+ {
+ $params['content_type'] = 'multipart/alternative';
+ if (is_object($obj)) {
+ return $obj->addSubpart('', $params);
+ } else {
+ $ret = new Mail_mimePart('', $params);
+ return $ret;
+ }
+ }
+
+ /**
+ * Adds a multipart/related part to a mimePart
+ * object (or creates one), and returns it during
+ * the build process.
+ *
+ * @param mixed The object to add the part to, or
+ * null if a new object is to be created
+ * @return object The multipart/mixed mimePart object
+ * @access private
+ */
+ function &_addRelatedPart(&$obj)
+ {
+ $params['content_type'] = 'multipart/related';
+ if (is_object($obj)) {
+ return $obj->addSubpart('', $params);
+ } else {
+ $ret = new Mail_mimePart('', $params);
+ return $ret;
+ }
+ }
+
+ /**
+ * Adds an html image subpart to a mimePart object
+ * and returns it during the build process.
+ *
+ * @param object The mimePart to add the image to
+ * @param array The image information
+ * @return object The image mimePart object
+ * @access private
+ */
+ function &_addHtmlImagePart(&$obj, $value)
+ {
+ $params['content_type'] = $value['c_type'] . '; ' .
+ 'name="' . $value['name'] . '"';
+ $params['encoding'] = 'base64';
+ $params['disposition'] = 'inline';
+ $params['dfilename'] = $value['name'];
+ $params['cid'] = $value['cid'];
+ $ret = $obj->addSubpart($value['body'], $params);
+ return $ret;
+
+ }
+
+ /**
+ * Adds an attachment subpart to a mimePart object
+ * and returns it during the build process.
+ *
+ * @param object The mimePart to add the image to
+ * @param array The attachment information
+ * @return object The image mimePart object
+ * @access private
+ */
+ function &_addAttachmentPart(&$obj, $value)
+ {
+ $params['dfilename'] = $value['name'];
+ $params['encoding'] = $value['encoding'];
+ if ($value['disposition'] != "inline") {
+ $fname = array("fname" => $value['name']);
+ $fname_enc = $this->_encodeHeaders($fname);
+ $params['dfilename'] = $fname_enc['fname'];
+ }
+ if ($value['charset']) {
+ $params['charset'] = $value['charset'];
+ }
+ $params['content_type'] = $value['c_type'] . '; ' .
+ 'name="' . $params['dfilename'] . '"';
+ $params['disposition'] = isset($value['disposition']) ?
+ $value['disposition'] : 'attachment';
+ $ret = $obj->addSubpart($value['body'], $params);
+ return $ret;
+ }
+
+ /**
+ * Returns the complete e-mail, ready to send using an alternative
+ * mail delivery method. Note that only the mailpart that is made
+ * with Mail_Mime is created. This means that,
+ * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF
+ * using the $xtra_headers parameter!
+ *
+ * @param string $separation The separation etween these two parts.
+ * @param array $build_params The Build parameters passed to the
+ * &get() function. See &get for more info.
+ * @param array $xtra_headers The extra headers that should be passed
+ * to the &headers() function.
+ * See that function for more info.
+ * @param bool $overwrite Overwrite the existing headers with new.
+ * @return string The complete e-mail.
+ * @access public
+ */
+ function getMessage($separation = null, $build_params = null, $xtra_headers = null, $overwrite = false)
+ {
+ if ($separation === null)
+ {
+ $separation = MAIL_MIME_CRLF;
+ }
+ $body = $this->get($build_params);
+ $head = $this->txtHeaders($xtra_headers, $overwrite);
+ $mail = $head . $separation . $body;
+ return $mail;
+ }
+
+
+ /**
+ * Builds the multipart message from the list ($this->_parts) and
+ * returns the mime content.
+ *
+ * @param array Build parameters that change the way the email
+ * is built. Should be associative. Can contain:
+ * head_encoding - What encoding to use for the headers.
+ * Options: quoted-printable or base64
+ * Default is quoted-printable
+ * text_encoding - What encoding to use for plain text
+ * Options: 7bit, 8bit, base64, or quoted-printable
+ * Default is 7bit
+ * html_encoding - What encoding to use for html
+ * Options: 7bit, 8bit, base64, or quoted-printable
+ * Default is quoted-printable
+ * 7bit_wrap - Number of characters before text is
+ * wrapped in 7bit encoding
+ * Default is 998
+ * html_charset - The character set to use for html.
+ * Default is iso-8859-1
+ * text_charset - The character set to use for text.
+ * Default is iso-8859-1
+ * head_charset - The character set to use for headers.
+ * Default is iso-8859-1
+ * @return string The mime content
+ * @access public
+ */
+ function &get($build_params = null)
+ {
+ if (isset($build_params)) {
+ while (list($key, $value) = each($build_params)) {
+ $this->_build_params[$key] = $value;
+ }
+ }
+
+ if (!empty($this->_html_images) AND isset($this->_htmlbody)) {
+ foreach ($this->_html_images as $key => $value) {
+ $regex = array();
+ $regex[] = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' .
+ preg_quote($value['name'], '#') . '\3#';
+ $regex[] = '#(?i)url(?-i)\(\s*(["\']?)' .
+ preg_quote($value['name'], '#') . '\1\s*\)#';
+ $rep = array();
+ $rep[] = '\1\2=\3cid:' . $value['cid'] .'\3';
+ $rep[] = 'url(\1cid:' . $value['cid'] . '\2)';
+ $this->_htmlbody = preg_replace($regex, $rep,
+ $this->_htmlbody
+ );
+ $this->_html_images[$key]['name'] = basename($this->_html_images[$key]['name']);
+ }
+ }
+
+ $null = null;
+ $attachments = !empty($this->_parts) ? true : false;
+ $html_images = !empty($this->_html_images) ? true : false;
+ $html = !empty($this->_htmlbody) ? true : false;
+ $text = (!$html AND !empty($this->_txtbody)) ? true : false;
+
+ switch (true) {
+ case $text AND !$attachments:
+ $message =& $this->_addTextPart($null, $this->_txtbody);
+ break;
+
+ case !$text AND !$html AND $attachments:
+ $message =& $this->_addMixedPart();
+ for ($i = 0; $i < count($this->_parts); $i++) {
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
+ }
+ break;
+
+ case $text AND $attachments:
+ $message =& $this->_addMixedPart();
+ $this->_addTextPart($message, $this->_txtbody);
+ for ($i = 0; $i < count($this->_parts); $i++) {
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
+ }
+ break;
+
+ case $html AND !$attachments AND !$html_images:
+ if (isset($this->_txtbody)) {
+ $message =& $this->_addAlternativePart($null);
+ $this->_addTextPart($message, $this->_txtbody);
+ $this->_addHtmlPart($message);
+ } else {
+ $message =& $this->_addHtmlPart($null);
+ }
+ break;
+
+ case $html AND !$attachments AND $html_images:
+ if (isset($this->_txtbody)) {
+ $message =& $this->_addAlternativePart($null);
+ $this->_addTextPart($message, $this->_txtbody);
+ $related =& $this->_addRelatedPart($message);
+ } else {
+ $message =& $this->_addRelatedPart($null);
+ $related =& $message;
+ }
+ $this->_addHtmlPart($related);
+ for ($i = 0; $i < count($this->_html_images); $i++) {
+ $this->_addHtmlImagePart($related, $this->_html_images[$i]);
+ }
+ break;
+
+ case $html AND $attachments AND !$html_images:
+ $message =& $this->_addMixedPart();
+ if (isset($this->_txtbody)) {
+ $alt =& $this->_addAlternativePart($message);
+ $this->_addTextPart($alt, $this->_txtbody);
+ $this->_addHtmlPart($alt);
+ } else {
+ $this->_addHtmlPart($message);
+ }
+ for ($i = 0; $i < count($this->_parts); $i++) {
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
+ }
+ break;
+
+ case $html AND $attachments AND $html_images:
+ $message =& $this->_addMixedPart();
+ if (isset($this->_txtbody)) {
+ $alt =& $this->_addAlternativePart($message);
+ $this->_addTextPart($alt, $this->_txtbody);
+ $rel =& $this->_addRelatedPart($alt);
+ } else {
+ $rel =& $this->_addRelatedPart($message);
+ }
+ $this->_addHtmlPart($rel);
+ for ($i = 0; $i < count($this->_html_images); $i++) {
+ $this->_addHtmlImagePart($rel, $this->_html_images[$i]);
+ }
+ for ($i = 0; $i < count($this->_parts); $i++) {
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
+ }
+ break;
+
+ }
+
+ if (isset($message)) {
+ $output = $message->encode();
+ $this->_headers = array_merge($this->_headers,
+ $output['headers']);
+ $body = $output['body'];
+ return $body;
+
+ } else {
+ $ret = false;
+ return $ret;
+ }
+ }
+
+ /**
+ * Returns an array with the headers needed to prepend to the email
+ * (MIME-Version and Content-Type). Format of argument is:
+ * $array['header-name'] = 'header-value';
+ *
+ * @param array $xtra_headers Assoc array with any extra headers.
+ * Optional.
+ * @param bool $overwrite Overwrite already existing headers.
+ * @return array Assoc array with the mime headers
+ * @access public
+ */
+ function &headers($xtra_headers = null, $overwrite = false)
+ {
+ // Content-Type header should already be present,
+ // So just add mime version header
+ $headers['MIME-Version'] = '1.0';
+ if (isset($xtra_headers)) {
+ $headers = array_merge($headers, $xtra_headers);
+ }
+ if ($overwrite){
+ $this->_headers = array_merge($this->_headers, $headers);
+ }else{
+ $this->_headers = array_merge($headers, $this->_headers);
+ }
+
+ $encodedHeaders = $this->_encodeHeaders($this->_headers);
+ return $encodedHeaders;
+ }
+
+ /**
+ * Get the text version of the headers
+ * (usefull if you want to use the PHP mail() function)
+ *
+ * @param array $xtra_headers Assoc array with any extra headers.
+ * Optional.
+ * @param bool $overwrite Overwrite the existing heaers with new.
+ * @return string Plain text headers
+ * @access public
+ */
+ function txtHeaders($xtra_headers = null, $overwrite = false)
+ {
+ $headers = $this->headers($xtra_headers, $overwrite);
+ $ret = '';
+ foreach ($headers as $key => $val) {
+ $ret .= "$key: $val" . MAIL_MIME_CRLF;
+ }
+ return $ret;
+ }
+
+ /**
+ * Sets the Subject header
+ *
+ * @param string $subject String to set the subject to
+ * access public
+ */
+ function setSubject($subject)
+ {
+ $this->_headers['Subject'] = $subject;
+ }
+
+ /**
+ * Set an email to the From (the sender) header
+ *
+ * @param string $email The email direction to add
+ * @access public
+ */
+ function setFrom($email)
+ {
+ $this->_headers['From'] = $email;
+ }
+
+ /**
+ * Add an email to the Cc (carbon copy) header
+ * (multiple calls to this method are allowed)
+ *
+ * @param string $email The email direction to add
+ * @access public
+ */
+ function addCc($email)
+ {
+ if (isset($this->_headers['Cc'])) {
+ $this->_headers['Cc'] .= ", $email";
+ } else {
+ $this->_headers['Cc'] = $email;
+ }
+ }
+
+ /**
+ * Add an email to the Bcc (blank carbon copy) header
+ * (multiple calls to this method are allowed)
+ *
+ * @param string $email The email direction to add
+ * @access public
+ */
+ function addBcc($email)
+ {
+ if (isset($this->_headers['Bcc'])) {
+ $this->_headers['Bcc'] .= ", $email";
+ } else {
+ $this->_headers['Bcc'] = $email;
+ }
+ }
+
+ /**
+ * Since the PHP send function requires you to specifiy
+ * recipients (To: header) separately from the other
+ * headers, the To: header is not properly encoded.
+ * To fix this, you can use this public method to
+ * encode your recipients before sending to the send
+ * function
+ *
+ * @param string $recipients A comma-delimited list of recipients
+ * @return string Encoded data
+ * @access public
+ */
+ function encodeRecipients($recipients)
+ {
+ $input = array("To" => $recipients);
+ $retval = $this->_encodeHeaders($input);
+ return $retval["To"] ;
+ }
+
+ /**
+ * Encodes a header as per RFC2047
+ *
+ * @param array $input The header data to encode
+ * @return array Encoded data
+ * @access private
+ */
+ function _encodeHeaders($input)
+ {
+ foreach ($input as $hdr_name => $hdr_value) {
+ if (function_exists('iconv_mime_encode') && preg_match('#[\x80-\xFF]{1}#', $hdr_value)){
+ $imePref = array();
+ if ($this->_build_params['head_encoding'] == 'base64'){
+ $imePrefs['scheme'] = 'B';
+ }else{
+ $imePrefs['scheme'] = 'Q';
+ }
+ $imePrefs['input-charset'] = $this->_build_params['head_charset'];
+ $imePrefs['output-charset'] = $this->_build_params['head_charset'];
+ $hdr_value = iconv_mime_encode($hdr_name, $hdr_value, $imePrefs);
+ $hdr_value = preg_replace("#^{$hdr_name}\:\ #", "", $hdr_value);
+ }elseif (preg_match('#[\x80-\xFF]{1}#', $hdr_value)){
+ //This header contains non ASCII chars and should be encoded.
+ switch ($this->_build_params['head_encoding']) {
+ case 'base64':
+ //Base64 encoding has been selected.
+
+ //Generate the header using the specified params and dynamicly
+ //determine the maximum length of such strings.
+ //75 is the value specified in the RFC. The -2 is there so
+ //the later regexp doesn't break any of the translated chars.
+ $prefix = '=?' . $this->_build_params['head_charset'] . '?B?';
+ $suffix = '?=';
+ $maxLength = 75 - strlen($prefix . $suffix) - 2;
+ $maxLength1stLine = $maxLength - strlen($hdr_name);
+
+ //Base64 encode the entire string
+ $hdr_value = base64_encode($hdr_value);
+
+ //This regexp will break base64-encoded text at every
+ //$maxLength but will not break any encoded letters.
+ $reg1st = "|.{0,$maxLength1stLine}[^\=][^\=]|";
+ $reg2nd = "|.{0,$maxLength}[^\=][^\=]|";
+ break;
+ case 'quoted-printable':
+ default:
+ //quoted-printable encoding has been selected
+
+ //Generate the header using the specified params and dynamicly
+ //determine the maximum length of such strings.
+ //75 is the value specified in the RFC. The -2 is there so
+ //the later regexp doesn't break any of the translated chars.
+ $prefix = '=?' . $this->_build_params['head_charset'] . '?Q?';
+ $suffix = '?=';
+ $maxLength = 75 - strlen($prefix . $suffix) - 2;
+ $maxLength1stLine = $maxLength - strlen($hdr_name);
+
+ //Replace all special characters used by the encoder.
+ $search = array("=", "_", "?", " ");
+ $replace = array("=3D", "=5F", "=3F", "_");
+ $hdr_value = str_replace($search, $replace, $hdr_value);
+
+ //Replace all extended characters (\x80-xFF) with their
+ //ASCII values.
+ $hdr_value = preg_replace(
+ '#([\x80-\xFF])#e',
+ '"=" . strtoupper(dechex(ord("\1")))',
+ $hdr_value
+ );
+ //This regexp will break QP-encoded text at every $maxLength
+ //but will not break any encoded letters.
+ $reg1st = "|(.{0,$maxLength})[^\=]|";
+ $reg2nd = "|(.{0,$maxLength})[^\=]|";
+ break;
+ }
+ //Begin with the regexp for the first line.
+ $reg = $reg1st;
+ $output = "";
+ while ($hdr_value) {
+ //Split translated string at every $maxLength
+ //But make sure not to break any translated chars.
+ $found = preg_match($reg, $hdr_value, $matches);
+
+ //After this first line, we need to use a different
+ //regexp for the first line.
+ $reg = $reg2nd;
+
+ //Save the found part and encapsulate it in the
+ //prefix & suffix. Then remove the part from the
+ //$hdr_value variable.
+ if ($found){
+ $part = $matches[0];
+ $hdr_value = substr($hdr_value, strlen($matches[0]));
+ }else{
+ $part = $hdr_value;
+ $hdr_value = "";
+ }
+
+ //RFC 2047 specifies that any split header should be seperated
+ //by a CRLF SPACE.
+ if ($output){
+ $output .= "\r\n ";
+ }
+ $output .= $prefix . $part . $suffix;
+ }
+ $hdr_value = $output;
+ }
+ $input[$hdr_name] = $hdr_value;
+ }
+
+ return $input;
+ }
+
+ /**
+ * Set the object's end-of-line and define the constant if applicable
+ *
+ * @param string $eol End Of Line sequence
+ * @access private
+ */
+ function _setEOL($eol)
+ {
+ $this->_eol = $eol;
+ if (!defined('MAIL_MIME_CRLF')) {
+ define('MAIL_MIME_CRLF', $this->_eol, true);
+ }
+ }
+
+
+
+} // End of class
+?>
+
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | Copyright (c) 2003-2005 The PHP Group |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@phpguru.org> |
+// +-----------------------------------------------------------------------+
+
+require_once 'PEAR.php';
+
+/**
+* +----------------------------- IMPORTANT ------------------------------+
+* | Usage of this class compared to native php extensions such as |
+* | mailparse or imap, is slow and may be feature deficient. If available|
+* | you are STRONGLY recommended to use the php extensions. |
+* +----------------------------------------------------------------------+
+*
+* Mime Decoding class
+*
+* This class will parse a raw mime email and return
+* the structure. Returned structure is similar to
+* that returned by imap_fetchstructure().
+*
+* USAGE: (assume $input is your raw email)
+*
+* $decode = new Mail_mimeDecode($input, "\r\n");
+* $structure = $decode->decode();
+* print_r($structure);
+*
+* Or statically:
+*
+* $params['input'] = $input;
+* $structure = Mail_mimeDecode::decode($params);
+* print_r($structure);
+*
+* TODO:
+* o UTF8: ???
+
+ > 4. We have also found a solution for decoding the UTF-8
+ > headers. Therefore I made the following function:
+ >
+ > function decode_utf8($txt) {
+ > $trans=array("Å‘"=>"õ","ű"=>"û","Å\90"=>"Õ","Å°"
+ =>"Û");
+ > $txt=strtr($txt,$trans);
+ > return(utf8_decode($txt));
+ > }
+ >
+ > And I have inserted the following line to the class:
+ >
+ > if (strtolower($charset)=="utf-8") $text=decode_utf8($text);
+ >
+ > ... before the following one in the "_decodeHeader" function:
+ >
+ > $input = str_replace($encoded, $text, $input);
+ >
+ > This way from now on it can easily decode the UTF-8 headers too.
+
+*
+* @author Richard Heyes <richard@phpguru.org>
+* @version $Revision: 202 $
+* @package Mail
+*/
+class Mail_mimeDecode extends PEAR
+{
+ /**
+ * The raw email to decode
+ * @var string
+ */
+ var $_input;
+
+ /**
+ * The header part of the input
+ * @var string
+ */
+ var $_header;
+
+ /**
+ * The body part of the input
+ * @var string
+ */
+ var $_body;
+
+ /**
+ * If an error occurs, this is used to store the message
+ * @var string
+ */
+ var $_error;
+
+ /**
+ * Flag to determine whether to include bodies in the
+ * returned object.
+ * @var boolean
+ */
+ var $_include_bodies;
+
+ /**
+ * Flag to determine whether to decode bodies
+ * @var boolean
+ */
+ var $_decode_bodies;
+
+ /**
+ * Flag to determine whether to decode headers
+ * @var boolean
+ */
+ var $_decode_headers;
+
+ /**
+ * Constructor.
+ *
+ * Sets up the object, initialise the variables, and splits and
+ * stores the header and body of the input.
+ *
+ * @param string The input to decode
+ * @access public
+ */
+ function Mail_mimeDecode($input)
+ {
+ list($header, $body) = $this->_splitBodyHeader($input);
+
+ $this->_input = $input;
+ $this->_header = $header;
+ $this->_body = $body;
+ $this->_decode_bodies = false;
+ $this->_include_bodies = true;
+ }
+
+ /**
+ * Begins the decoding process. If called statically
+ * it will create an object and call the decode() method
+ * of it.
+ *
+ * @param array An array of various parameters that determine
+ * various things:
+ * include_bodies - Whether to include the body in the returned
+ * object.
+ * decode_bodies - Whether to decode the bodies
+ * of the parts. (Transfer encoding)
+ * decode_headers - Whether to decode headers
+ * input - If called statically, this will be treated
+ * as the input
+ * @return object Decoded results
+ * @access public
+ */
+ function decode($params = null)
+ {
+ // determine if this method has been called statically
+ $isStatic = !(isset($this) && get_class($this) == __CLASS__);
+
+ // Have we been called statically?
+ // If so, create an object and pass details to that.
+ if ($isStatic AND isset($params['input'])) {
+
+ $obj = new Mail_mimeDecode($params['input']);
+ $structure = $obj->decode($params);
+
+ // Called statically but no input
+ } elseif ($isStatic) {
+ return PEAR::raiseError('Called statically and no input given');
+
+ // Called via an object
+ } else {
+ $this->_include_bodies = isset($params['include_bodies']) ?
+ $params['include_bodies'] : false;
+ $this->_decode_bodies = isset($params['decode_bodies']) ?
+ $params['decode_bodies'] : false;
+ $this->_decode_headers = isset($params['decode_headers']) ?
+ $params['decode_headers'] : false;
+
+ $structure = $this->_decode($this->_header, $this->_body);
+ if ($structure === false) {
+ $structure = $this->raiseError($this->_error);
+ }
+ }
+
+ return $structure;
+ }
+
+ /**
+ * Performs the decoding. Decodes the body string passed to it
+ * If it finds certain content-types it will call itself in a
+ * recursive fashion
+ *
+ * @param string Header section
+ * @param string Body section
+ * @return object Results of decoding process
+ * @access private
+ */
+ function _decode($headers, $body, $default_ctype = 'text/plain')
+ {
+ $return = new stdClass;
+ $return->headers = array();
+ $headers = $this->_parseHeaders($headers);
+
+ foreach ($headers as $value) {
+ if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
+ $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
+ $return->headers[strtolower($value['name'])][] = $value['value'];
+
+ } elseif (isset($return->headers[strtolower($value['name'])])) {
+ $return->headers[strtolower($value['name'])][] = $value['value'];
+
+ } else {
+ $return->headers[strtolower($value['name'])] = $value['value'];
+ }
+ }
+
+ reset($headers);
+ while (list($key, $value) = each($headers)) {
+ $headers[$key]['name'] = strtolower($headers[$key]['name']);
+ switch ($headers[$key]['name']) {
+
+ case 'content-type':
+ $content_type = $this->_parseHeaderValue($headers[$key]['value']);
+
+ if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
+ $return->ctype_primary = $regs[1];
+ $return->ctype_secondary = $regs[2];
+ }
+
+ if (isset($content_type['other'])) {
+ while (list($p_name, $p_value) = each($content_type['other'])) {
+ $return->ctype_parameters[$p_name] = $p_value;
+ }
+ }
+ break;
+
+ case 'content-disposition':
+ $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
+ $return->disposition = $content_disposition['value'];
+ if (isset($content_disposition['other'])) {
+ while (list($p_name, $p_value) = each($content_disposition['other'])) {
+ $return->d_parameters[$p_name] = $p_value;
+ }
+ }
+ break;
+
+ case 'content-transfer-encoding':
+ $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
+ break;
+ }
+ }
+
+ if (isset($content_type)) {
+ switch (strtolower($content_type['value'])) {
+ case 'text/plain':
+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
+ break;
+
+ case 'text/html':
+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
+ break;
+
+ case 'multipart/parallel':
+ case 'multipart/appledouble': // Appledouble mail
+ case 'multipart/report': // RFC1892
+ case 'multipart/signed': // PGP
+ case 'multipart/digest':
+ case 'multipart/alternative':
+ case 'multipart/related':
+ case 'multipart/mixed':
+ if(!isset($content_type['other']['boundary'])){
+ $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
+ return false;
+ }
+
+ $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
+
+ $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
+ for ($i = 0; $i < count($parts); $i++) {
+ list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
+ $part = $this->_decode($part_header, $part_body, $default_ctype);
+ if($part === false)
+ $part = $this->raiseError($this->_error);
+ $return->parts[] = $part;
+ }
+ break;
+
+ case 'message/rfc822':
+ $obj = &new Mail_mimeDecode($body);
+ $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
+ 'decode_bodies' => $this->_decode_bodies,
+ 'decode_headers' => $this->_decode_headers));
+ unset($obj);
+ break;
+
+ default:
+ if(!isset($content_transfer_encoding['value']))
+ $content_transfer_encoding['value'] = '7bit';
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
+ break;
+ }
+
+ } else {
+ $ctype = explode('/', $default_ctype);
+ $return->ctype_primary = $ctype[0];
+ $return->ctype_secondary = $ctype[1];
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
+ }
+
+ return $return;
+ }
+
+ /**
+ * Given the output of the above function, this will return an
+ * array of references to the parts, indexed by mime number.
+ *
+ * @param object $structure The structure to go through
+ * @param string $mime_number Internal use only.
+ * @return array Mime numbers
+ */
+ function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
+ {
+ $return = array();
+ if (!empty($structure->parts)) {
+ if ($mime_number != '') {
+ $structure->mime_id = $prepend . $mime_number;
+ $return[$prepend . $mime_number] = &$structure;
+ }
+ for ($i = 0; $i < count($structure->parts); $i++) {
+
+
+ if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
+ $prepend = $prepend . $mime_number . '.';
+ $_mime_number = '';
+ } else {
+ $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
+ }
+
+ $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
+ foreach ($arr as $key => $val) {
+ $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
+ }
+ }
+ } else {
+ if ($mime_number == '') {
+ $mime_number = '1';
+ }
+ $structure->mime_id = $prepend . $mime_number;
+ $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
+ }
+
+ return $return;
+ }
+
+ /**
+ * Given a string containing a header and body
+ * section, this function will split them (at the first
+ * blank line) and return them.
+ *
+ * @param string Input to split apart
+ * @return array Contains header and body section
+ * @access private
+ */
+ function _splitBodyHeader($input)
+ {
+ if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
+ return array($match[1], $match[2]);
+ }
+ $this->_error = 'Could not split header and body';
+ return false;
+ }
+
+ /**
+ * Parse headers given in $input and return
+ * as assoc array.
+ *
+ * @param string Headers to parse
+ * @return array Contains parsed headers
+ * @access private
+ */
+ function _parseHeaders($input)
+ {
+
+ if ($input !== '') {
+ // Unfold the input
+ $input = preg_replace("/\r?\n/", "\r\n", $input);
+ $input = preg_replace("/\r\n(\t| )+/", ' ', $input);
+ $headers = explode("\r\n", trim($input));
+
+ foreach ($headers as $value) {
+ $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
+ $hdr_value = substr($value, $pos+1);
+ if($hdr_value[0] == ' ')
+ $hdr_value = substr($hdr_value, 1);
+
+ $return[] = array(
+ 'name' => $hdr_name,
+ 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
+ );
+ }
+ } else {
+ $return = array();
+ }
+
+ return $return;
+ }
+
+ /**
+ * Function to parse a header value,
+ * extract first part, and any secondary
+ * parts (after ;) This function is not as
+ * robust as it could be. Eg. header comments
+ * in the wrong place will probably break it.
+ *
+ * @param string Header value to parse
+ * @return array Contains parsed result
+ * @access private
+ */
+ function _parseHeaderValue($input)
+ {
+
+ if (($pos = strpos($input, ';')) !== false) {
+
+ $return['value'] = trim(substr($input, 0, $pos));
+ $input = trim(substr($input, $pos+1));
+
+ if (strlen($input) > 0) {
+
+ // This splits on a semi-colon, if there's no preceeding backslash
+ // Now works with quoted values; had to glue the \; breaks in PHP
+ // the regex is already bordering on incomprehensible
+ $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
+ preg_match_all($splitRegex, $input, $matches);
+ $parameters = array();
+ for ($i=0; $i<count($matches[0]); $i++) {
+ $param = $matches[0][$i];
+ while (substr($param, -2) == '\;') {
+ $param .= $matches[0][++$i];
+ }
+ $parameters[] = $param;
+ }
+
+ for ($i = 0; $i < count($parameters); $i++) {
+ $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
+ $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
+ if ($param_value[0] == '"') {
+ $param_value = substr($param_value, 1, -1);
+ }
+ $return['other'][$param_name] = $param_value;
+ $return['other'][strtolower($param_name)] = $param_value;
+ }
+ }
+ } else {
+ $return['value'] = trim($input);
+ }
+
+ return $return;
+ }
+
+ /**
+ * This function splits the input based
+ * on the given boundary
+ *
+ * @param string Input to parse
+ * @return array Contains array of resulting mime parts
+ * @access private
+ */
+ function _boundarySplit($input, $boundary)
+ {
+ $parts = array();
+
+ $bs_possible = substr($boundary, 2, -2);
+ $bs_check = '\"' . $bs_possible . '\"';
+
+ if ($boundary == $bs_check) {
+ $boundary = $bs_possible;
+ }
+
+ $tmp = explode('--' . $boundary, $input);
+
+ for ($i = 1; $i < count($tmp) - 1; $i++) {
+ $parts[] = $tmp[$i];
+ }
+
+ return $parts;
+ }
+
+ /**
+ * Given a header, this function will decode it
+ * according to RFC2047. Probably not *exactly*
+ * conformant, but it does pass all the given
+ * examples (in RFC2047).
+ *
+ * @param string Input header value to decode
+ * @return string Decoded header value
+ * @access private
+ */
+ function _decodeHeader($input)
+ {
+ // Remove white space between encoded-words
+ $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
+
+ // For each encoded-word...
+ while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
+
+ $encoded = $matches[1];
+ $charset = $matches[2];
+ $encoding = $matches[3];
+ $text = $matches[4];
+
+ switch (strtolower($encoding)) {
+ case 'b':
+ $text = base64_decode($text);
+ break;
+
+ case 'q':
+ $text = str_replace('_', ' ', $text);
+ preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
+ foreach($matches[1] as $value)
+ $text = str_replace('='.$value, chr(hexdec($value)), $text);
+ break;
+ }
+
+ $input = str_replace($encoded, $text, $input);
+ }
+
+ return $input;
+ }
+
+ /**
+ * Given a body string and an encoding type,
+ * this function will decode and return it.
+ *
+ * @param string Input body to decode
+ * @param string Encoding type to use.
+ * @return string Decoded body
+ * @access private
+ */
+ function _decodeBody($input, $encoding = '7bit')
+ {
+ switch (strtolower($encoding)) {
+ case '7bit':
+ return $input;
+ break;
+
+ case 'quoted-printable':
+ return $this->_quotedPrintableDecode($input);
+ break;
+
+ case 'base64':
+ return base64_decode($input);
+ break;
+
+ default:
+ return $input;
+ }
+ }
+
+ /**
+ * Given a quoted-printable string, this
+ * function will decode and return it.
+ *
+ * @param string Input body to decode
+ * @return string Decoded body
+ * @access private
+ */
+ function _quotedPrintableDecode($input)
+ {
+ // Remove soft line breaks
+ $input = preg_replace("/=\r?\n/", '', $input);
+
+ // Replace encoded characters
+ $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
+
+ return $input;
+ }
+
+ /**
+ * Checks the input for uuencoded files and returns
+ * an array of them. Can be called statically, eg:
+ *
+ * $files =& Mail_mimeDecode::uudecode($some_text);
+ *
+ * It will check for the begin 666 ... end syntax
+ * however and won't just blindly decode whatever you
+ * pass it.
+ *
+ * @param string Input body to look for attahcments in
+ * @return array Decoded bodies, filenames and permissions
+ * @access public
+ * @author Unknown
+ */
+ function &uudecode($input)
+ {
+ // Find all uuencoded sections
+ preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
+
+ for ($j = 0; $j < count($matches[3]); $j++) {
+
+ $str = $matches[3][$j];
+ $filename = $matches[2][$j];
+ $fileperm = $matches[1][$j];
+
+ $file = '';
+ $str = preg_split("/\r?\n/", trim($str));
+ $strlen = count($str);
+
+ for ($i = 0; $i < $strlen; $i++) {
+ $pos = 1;
+ $d = 0;
+ $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
+
+ while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+ $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
+ $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+ $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
+
+ $file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077));
+
+ $pos += 4;
+ $d += 3;
+ }
+
+ if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+ $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+ $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
+
+ $pos += 3;
+ $d += 2;
+ }
+
+ if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+ }
+ }
+ $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
+ }
+
+ return $files;
+ }
+
+ /**
+ * getSendArray() returns the arguments required for Mail::send()
+ * used to build the arguments for a mail::send() call
+ *
+ * Usage:
+ * $mailtext = Full email (for example generated by a template)
+ * $decoder = new Mail_mimeDecode($mailtext);
+ * $parts = $decoder->getSendArray();
+ * if (!PEAR::isError($parts) {
+ * list($recipents,$headers,$body) = $parts;
+ * $mail = Mail::factory('smtp');
+ * $mail->send($recipents,$headers,$body);
+ * } else {
+ * echo $parts->message;
+ * }
+ * @return mixed array of recipeint, headers,body or Pear_Error
+ * @access public
+ * @author Alan Knowles <alan@akbkhome.com>
+ */
+ function getSendArray()
+ {
+ // prevent warning if this is not set
+ $this->_decode_headers = FALSE;
+ $headerlist =$this->_parseHeaders($this->_header);
+ $to = "";
+ if (!$headerlist) {
+ return $this->raiseError("Message did not contain headers");
+ }
+ foreach($headerlist as $item) {
+ $header[$item['name']] = $item['value'];
+ switch (strtolower($item['name'])) {
+ case "to":
+ case "cc":
+ case "bcc":
+ $to = ",".$item['value'];
+ default:
+ break;
+ }
+ }
+ if ($to == "") {
+ return $this->raiseError("Message did not contain any recipents");
+ }
+ $to = substr($to,1);
+ return array($to,$header,$this->_body);
+ }
+
+ /**
+ * Returns a xml copy of the output of
+ * Mail_mimeDecode::decode. Pass the output in as the
+ * argument. This function can be called statically. Eg:
+ *
+ * $output = $obj->decode();
+ * $xml = Mail_mimeDecode::getXML($output);
+ *
+ * The DTD used for this should have been in the package. Or
+ * alternatively you can get it from cvs, or here:
+ * http://www.phpguru.org/xmail/xmail.dtd.
+ *
+ * @param object Input to convert to xml. This should be the
+ * output of the Mail_mimeDecode::decode function
+ * @return string XML version of input
+ * @access public
+ */
+ function getXML($input)
+ {
+ $crlf = "\r\n";
+ $output = '<?xml version=\'1.0\'?>' . $crlf .
+ '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
+ '<email>' . $crlf .
+ Mail_mimeDecode::_getXML($input) .
+ '</email>';
+
+ return $output;
+ }
+
+ /**
+ * Function that does the actual conversion to xml. Does a single
+ * mimepart at a time.
+ *
+ * @param object Input to convert to xml. This is a mimepart object.
+ * It may or may not contain subparts.
+ * @param integer Number of tabs to indent
+ * @return string XML version of input
+ * @access private
+ */
+ function _getXML($input, $indent = 1)
+ {
+ $htab = "\t";
+ $crlf = "\r\n";
+ $output = '';
+ $headers = @(array)$input->headers;
+
+ foreach ($headers as $hdr_name => $hdr_value) {
+
+ // Multiple headers with this name
+ if (is_array($headers[$hdr_name])) {
+ for ($i = 0; $i < count($hdr_value); $i++) {
+ $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
+ }
+
+ // Only one header of this sort
+ } else {
+ $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
+ }
+ }
+
+ if (!empty($input->parts)) {
+ for ($i = 0; $i < count($input->parts); $i++) {
+ $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
+ Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
+ str_repeat($htab, $indent) . '</mimepart>' . $crlf;
+ }
+ } elseif (isset($input->body)) {
+ $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
+ $input->body . ']]></body>' . $crlf;
+ }
+
+ return $output;
+ }
+
+ /**
+ * Helper function to _getXML(). Returns xml of a header.
+ *
+ * @param string Name of header
+ * @param string Value of header
+ * @param integer Number of tabs to indent
+ * @return string XML version of input
+ * @access private
+ */
+ function _getXML_helper($hdr_name, $hdr_value, $indent)
+ {
+ $htab = "\t";
+ $crlf = "\r\n";
+ $return = '';
+
+ $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
+ $new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
+
+ // Sort out any parameters
+ if (!empty($new_hdr_value['other'])) {
+ foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
+ $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
+ str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
+ str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
+ str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
+ }
+
+ $params = implode('', $params);
+ } else {
+ $params = '';
+ }
+
+ $return = str_repeat($htab, $indent) . '<header>' . $crlf .
+ str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
+ str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
+ $params .
+ str_repeat($htab, $indent) . '</header>' . $crlf;
+
+ return $return;
+ }
+
+} // End of class
+?>
--- /dev/null
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard@phpguru.org> |
+// +-----------------------------------------------------------------------+
+
+/**
+*
+* Raw mime encoding class
+*
+* What is it?
+* This class enables you to manipulate and build
+* a mime email from the ground up.
+*
+* Why use this instead of mime.php?
+* mime.php is a userfriendly api to this class for
+* people who aren't interested in the internals of
+* mime mail. This class however allows full control
+* over the email.
+*
+* Eg.
+*
+* // Since multipart/mixed has no real body, (the body is
+* // the subpart), we set the body argument to blank.
+*
+* $params['content_type'] = 'multipart/mixed';
+* $email = new Mail_mimePart('', $params);
+*
+* // Here we add a text part to the multipart we have
+* // already. Assume $body contains plain text.
+*
+* $params['content_type'] = 'text/plain';
+* $params['encoding'] = '7bit';
+* $text = $email->addSubPart($body, $params);
+*
+* // Now add an attachment. Assume $attach is
+* the contents of the attachment
+*
+* $params['content_type'] = 'application/zip';
+* $params['encoding'] = 'base64';
+* $params['disposition'] = 'attachment';
+* $params['dfilename'] = 'example.zip';
+* $attach =& $email->addSubPart($body, $params);
+*
+* // Now build the email. Note that the encode
+* // function returns an associative array containing two
+* // elements, body and headers. You will need to add extra
+* // headers, (eg. Mime-Version) before sending.
+*
+* $email = $message->encode();
+* $email['headers'][] = 'Mime-Version: 1.0';
+*
+*
+* Further examples are available at http://www.phpguru.org
+*
+* TODO:
+* - Set encode() to return the $obj->encoded if encode()
+* has already been run. Unless a flag is passed to specifically
+* re-build the message.
+*
+* @author Richard Heyes <richard@phpguru.org>
+* @version $Revision: 202 $
+* @package Mail
+*/
+
+class Mail_mimePart {
+
+ /**
+ * The encoding type of this part
+ * @var string
+ */
+ var $_encoding;
+
+ /**
+ * An array of subparts
+ * @var array
+ */
+ var $_subparts;
+
+ /**
+ * The output of this part after being built
+ * @var string
+ */
+ var $_encoded;
+
+ /**
+ * Headers for this part
+ * @var array
+ */
+ var $_headers;
+
+ /**
+ * The body of this part (not encoded)
+ * @var string
+ */
+ var $_body;
+
+ /**
+ * Constructor.
+ *
+ * Sets up the object.
+ *
+ * @param $body - The body of the mime part if any.
+ * @param $params - An associative array of parameters:
+ * content_type - The content type for this part eg multipart/mixed
+ * encoding - The encoding to use, 7bit, 8bit, base64, or quoted-printable
+ * cid - Content ID to apply
+ * disposition - Content disposition, inline or attachment
+ * dfilename - Optional filename parameter for content disposition
+ * description - Content description
+ * charset - Character set to use
+ * @access public
+ */
+ function Mail_mimePart($body = '', $params = array())
+ {
+ if (!defined('MAIL_MIMEPART_CRLF')) {
+ define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE);
+ }
+
+ foreach ($params as $key => $value) {
+ switch ($key) {
+ case 'content_type':
+ $headers['Content-Type'] = $value . (isset($charset) ? '; charset="' . $charset . '"' : '');
+ break;
+
+ case 'encoding':
+ $this->_encoding = $value;
+ $headers['Content-Transfer-Encoding'] = $value;
+ break;
+
+ case 'cid':
+ $headers['Content-ID'] = '<' . $value . '>';
+ break;
+
+ case 'disposition':
+ $headers['Content-Disposition'] = $value . (isset($dfilename) ? '; filename="' . $dfilename . '"' : '');
+ break;
+
+ case 'dfilename':
+ if (isset($headers['Content-Disposition'])) {
+ $headers['Content-Disposition'] .= '; filename="' . $value . '"';
+ } else {
+ $dfilename = $value;
+ }
+ break;
+
+ case 'description':
+ $headers['Content-Description'] = $value;
+ break;
+
+ case 'charset':
+ if (isset($headers['Content-Type'])) {
+ $headers['Content-Type'] .= '; charset="' . $value . '"';
+ } else {
+ $charset = $value;
+ }
+ break;
+ }
+ }
+
+ // Default content-type
+ if (!isset($headers['Content-Type'])) {
+ $headers['Content-Type'] = 'text/plain';
+ }
+
+ //Default encoding
+ if (!isset($this->_encoding)) {
+ $this->_encoding = '7bit';
+ }
+
+ // Assign stuff to member variables
+ $this->_encoded = array();
+ $this->_headers = $headers;
+ $this->_body = $body;
+ }
+
+ /**
+ * encode()
+ *
+ * Encodes and returns the email. Also stores
+ * it in the encoded member variable
+ *
+ * @return An associative array containing two elements,
+ * body and headers. The headers element is itself
+ * an indexed array.
+ * @access public
+ */
+ function encode()
+ {
+ $encoded =& $this->_encoded;
+
+ if (!empty($this->_subparts)) {
+ srand((double)microtime()*1000000);
+ $boundary = '=_' . md5(rand() . microtime());
+ $this->_headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF . "\t" . 'boundary="' . $boundary . '"';
+
+ // Add body parts to $subparts
+ for ($i = 0; $i < count($this->_subparts); $i++) {
+ $headers = array();
+ $tmp = $this->_subparts[$i]->encode();
+ foreach ($tmp['headers'] as $key => $value) {
+ $headers[] = $key . ': ' . $value;
+ }
+ $subparts[] = implode(MAIL_MIMEPART_CRLF, $headers) . MAIL_MIMEPART_CRLF . MAIL_MIMEPART_CRLF . $tmp['body'];
+ }
+
+ $encoded['body'] = '--' . $boundary . MAIL_MIMEPART_CRLF .
+ implode('--' . $boundary . MAIL_MIMEPART_CRLF, $subparts) .
+ '--' . $boundary.'--' . MAIL_MIMEPART_CRLF;
+
+ } else {
+ $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding) . MAIL_MIMEPART_CRLF;
+ }
+
+ // Add headers to $encoded
+ $encoded['headers'] =& $this->_headers;
+
+ return $encoded;
+ }
+
+ /**
+ * &addSubPart()
+ *
+ * Adds a subpart to current mime part and returns
+ * a reference to it
+ *
+ * @param $body The body of the subpart, if any.
+ * @param $params The parameters for the subpart, same
+ * as the $params argument for constructor.
+ * @return A reference to the part you just added. It is
+ * crucial if using multipart/* in your subparts that
+ * you use =& in your script when calling this function,
+ * otherwise you will not be able to add further subparts.
+ * @access public
+ */
+ function &addSubPart($body, $params)
+ {
+ $this->_subparts[] = new Mail_mimePart($body, $params);
+ return $this->_subparts[count($this->_subparts) - 1];
+ }
+
+ /**
+ * _getEncodedData()
+ *
+ * Returns encoded data based upon encoding passed to it
+ *
+ * @param $data The data to encode.
+ * @param $encoding The encoding type to use, 7bit, base64,
+ * or quoted-printable.
+ * @access private
+ */
+ function _getEncodedData($data, $encoding)
+ {
+ switch ($encoding) {
+ case '8bit':
+ case '7bit':
+ return $data;
+ break;
+
+ case 'quoted-printable':
+ return $this->_quotedPrintableEncode($data);
+ break;
+
+ case 'base64':
+ return rtrim(chunk_split(base64_encode($data), 76, MAIL_MIMEPART_CRLF));
+ break;
+
+ default:
+ return $data;
+ }
+ }
+
+ /**
+ * quoteadPrintableEncode()
+ *
+ * Encodes data to quoted-printable standard.
+ *
+ * @param $input The data to encode
+ * @param $line_max Optional max line length. Should
+ * not be more than 76 chars
+ *
+ * @access private
+ */
+ function _quotedPrintableEncode($input , $line_max = 76)
+ {
+ $lines = preg_split("/\r?\n/", $input);
+ $eol = MAIL_MIMEPART_CRLF;
+ $escape = '=';
+ $output = '';
+
+ while(list(, $line) = each($lines)){
+
+ $linlen = strlen($line);
+ $newline = '';
+
+ for ($i = 0; $i < $linlen; $i++) {
+ $char = substr($line, $i, 1);
+ $dec = ord($char);
+
+ if (($dec == 32) AND ($i == ($linlen - 1))){ // convert space at eol only
+ $char = '=20';
+
+ } elseif(($dec == 9) AND ($i == ($linlen - 1))) { // convert tab at eol only
+ $char = '=09';
+ } elseif($dec == 9) {
+ ; // Do nothing if a tab.
+ } elseif(($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) {
+ $char = $escape . strtoupper(sprintf('%02s', dechex($dec)));
+ }
+
+ if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted
+ $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay
+ $newline = '';
+ }
+ $newline .= $char;
+ } // end of for
+ $output .= $newline . $eol;
+ }
+ $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf
+ return $output;
+ }
+} // End of class
+?>
--- /dev/null
+<?php
+/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Chuck Hagenbuch <chuck@horde.org> |
+// | Jon Parise <jon@php.net> |
+// | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
+// +----------------------------------------------------------------------+
+//
+// $Id: SMTP.php 17 2005-10-03 20:25:31Z roundcube $
+
+require_once 'PEAR.php';
+require_once 'Net/Socket.php';
+
+/**
+ * Provides an implementation of the SMTP protocol using PEAR's
+ * Net_Socket:: class.
+ *
+ * @package Net_SMTP
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jon Parise <jon@php.net>
+ * @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
+ *
+ * @example basic.php A basic implementation of the Net_SMTP package.
+ */
+class Net_SMTP
+{
+
+ /**
+ * The server to connect to.
+ * @var string
+ * @access public
+ */
+ var $host = 'localhost';
+
+ /**
+ * The port to connect to.
+ * @var int
+ * @access public
+ */
+ var $port = 25;
+
+ /**
+ * The value to give when sending EHLO or HELO.
+ * @var string
+ * @access public
+ */
+ var $localhost = 'localhost';
+
+ /**
+ * List of supported authentication methods, in preferential order.
+ * @var array
+ * @access public
+ */
+ var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN');
+
+ /**
+ * Should debugging output be enabled?
+ * @var boolean
+ * @access private
+ */
+ var $_debug = false;
+
+ /**
+ * The socket resource being used to connect to the SMTP server.
+ * @var resource
+ * @access private
+ */
+ var $_socket = null;
+
+ /**
+ * The most recent server response code.
+ * @var int
+ * @access private
+ */
+ var $_code = -1;
+
+ /**
+ * The most recent server response arguments.
+ * @var array
+ * @access private
+ */
+ var $_arguments = array();
+
+ /**
+ * Stores detected features of the SMTP server.
+ * @var array
+ * @access private
+ */
+ var $_esmtp = array();
+
+ /**
+ * Instantiates a new Net_SMTP object, overriding any defaults
+ * with parameters that are passed in.
+ *
+ * If you have SSL support in PHP, you can connect to a server
+ * over SSL using an 'ssl://' prefix:
+ *
+ * // 465 is a common smtps port.
+ * $smtp = new Net_SMTP('ssl://mail.host.com', 465);
+ * $smtp->connect();
+ *
+ * @param string $host The server to connect to.
+ * @param integer $port The port to connect to.
+ * @param string $localhost The value to give when sending EHLO or HELO.
+ *
+ * @access public
+ * @since 1.0
+ */
+ function Net_SMTP($host = null, $port = null, $localhost = null)
+ {
+ if (isset($host)) $this->host = $host;
+ if (isset($port)) $this->port = $port;
+ if (isset($localhost)) $this->localhost = $localhost;
+
+ $this->_socket = &new Net_Socket();
+
+ /*
+ * Include the Auth_SASL package. If the package is not available,
+ * we disable the authentication methods that depend upon it.
+ */
+ if ((@include_once 'Auth/SASL.php') === false) {
+ $pos = array_search('DIGEST-MD5', $this->auth_methods);
+ unset($this->auth_methods[$pos]);
+ $pos = array_search('CRAM-MD5', $this->auth_methods);
+ unset($this->auth_methods[$pos]);
+ }
+ }
+
+ /**
+ * Set the value of the debugging flag.
+ *
+ * @param boolean $debug New value for the debugging flag.
+ *
+ * @access public
+ * @since 1.1.0
+ */
+ function setDebug($debug)
+ {
+ $this->_debug = $debug;
+ }
+
+ /**
+ * Send the given string of data to the server.
+ *
+ * @param string $data The string of data to send.
+ *
+ * @return mixed True on success or a PEAR_Error object on failure.
+ *
+ * @access private
+ * @since 1.1.0
+ */
+ function _send($data)
+ {
+ if ($this->_debug) {
+ echo "DEBUG: Send: $data\n";
+ }
+
+ if (PEAR::isError($error = $this->_socket->write($data))) {
+ return PEAR::raiseError('Failed to write to socket: ' .
+ $error->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Send a command to the server with an optional string of
+ * arguments. A carriage return / linefeed (CRLF) sequence will
+ * be appended to each command string before it is sent to the
+ * SMTP server - an error will be thrown if the command string
+ * already contains any newline characters. Use _send() for
+ * commands that must contain newlines.
+ *
+ * @param string $command The SMTP command to send to the server.
+ * @param string $args A string of optional arguments to append
+ * to the command.
+ *
+ * @return mixed The result of the _send() call.
+ *
+ * @access private
+ * @since 1.1.0
+ */
+ function _put($command, $args = '')
+ {
+ if (!empty($args)) {
+ $command .= ' ' . $args;
+ }
+
+ if (strcspn($command, "\r\n") !== strlen($command)) {
+ return PEAR::raiseError('Commands cannot contain newlines');
+ }
+
+ return $this->_send($command . "\r\n");
+ }
+
+ /**
+ * Read a reply from the SMTP server. The reply consists of a response
+ * code and a response message.
+ *
+ * @param mixed $valid The set of valid response codes. These
+ * may be specified as an array of integer
+ * values or as a single integer value.
+ *
+ * @return mixed True if the server returned a valid response code or
+ * a PEAR_Error object is an error condition is reached.
+ *
+ * @access private
+ * @since 1.1.0
+ *
+ * @see getResponse
+ */
+ function _parseResponse($valid)
+ {
+ $this->_code = -1;
+ $this->_arguments = array();
+
+ while ($line = $this->_socket->readLine()) {
+ if ($this->_debug) {
+ echo "DEBUG: Recv: $line\n";
+ }
+
+ /* If we receive an empty line, the connection has been closed. */
+ if (empty($line)) {
+ $this->disconnect();
+ return PEAR::raiseError('Connection was unexpectedly closed');
+ }
+
+ /* Read the code and store the rest in the arguments array. */
+ $code = substr($line, 0, 3);
+ $this->_arguments[] = trim(substr($line, 4));
+
+ /* Check the syntax of the response code. */
+ if (is_numeric($code)) {
+ $this->_code = (int)$code;
+ } else {
+ $this->_code = -1;
+ break;
+ }
+
+ /* If this is not a multiline response, we're done. */
+ if (substr($line, 3, 1) != '-') {
+ break;
+ }
+ }
+
+ /* Compare the server's response code with the valid code. */
+ if (is_int($valid) && ($this->_code === $valid)) {
+ return true;
+ }
+
+ /* If we were given an array of valid response codes, check each one. */
+ if (is_array($valid)) {
+ foreach ($valid as $valid_code) {
+ if ($this->_code === $valid_code) {
+ return true;
+ }
+ }
+ }
+
+ return PEAR::raiseError('Invalid response code received from server');
+ }
+
+ /**
+ * Return a 2-tuple containing the last response from the SMTP server.
+ *
+ * @return array A two-element array: the first element contains the
+ * response code as an integer and the second element
+ * contains the response's arguments as a string.
+ *
+ * @access public
+ * @since 1.1.0
+ */
+ function getResponse()
+ {
+ return array($this->_code, join("\n", $this->_arguments));
+ }
+
+ /**
+ * Attempt to connect to the SMTP server.
+ *
+ * @param int $timeout The timeout value (in seconds) for the
+ * socket connection.
+ * @param bool $persistent Should a persistent socket connection
+ * be used?
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function connect($timeout = null, $persistent = false)
+ {
+ $result = $this->_socket->connect($this->host, $this->port,
+ $persistent, $timeout);
+ if (PEAR::isError($result)) {
+ return PEAR::raiseError('Failed to connect socket: ' .
+ $result->getMessage());
+ }
+
+ if (PEAR::isError($error = $this->_parseResponse(220))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_negotiate())) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempt to disconnect from the SMTP server.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function disconnect()
+ {
+ if (PEAR::isError($error = $this->_put('QUIT'))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(221))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_socket->disconnect())) {
+ return PEAR::raiseError('Failed to disconnect socket: ' .
+ $error->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempt to send the EHLO command and obtain a list of ESMTP
+ * extensions available, and failing that just send HELO.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access private
+ * @since 1.1.0
+ */
+ function _negotiate()
+ {
+ if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
+ return $error;
+ }
+
+ if (PEAR::isError($this->_parseResponse(250))) {
+ /* If we receive a 503 response, we're already authenticated. */
+ if ($this->_code === 503) {
+ return true;
+ }
+
+ /* If the EHLO failed, try the simpler HELO command. */
+ if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) {
+ return $error;
+ }
+ if (PEAR::isError($this->_parseResponse(250))) {
+ return PEAR::raiseError('HELO was not accepted: ', $this->_code);
+ }
+
+ return true;
+ }
+
+ foreach ($this->_arguments as $argument) {
+ $verb = strtok($argument, ' ');
+ $arguments = substr($argument, strlen($verb) + 1,
+ strlen($argument) - strlen($verb) - 1);
+ $this->_esmtp[$verb] = $arguments;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the name of the best authentication method that the server
+ * has advertised.
+ *
+ * @return mixed Returns a string containing the name of the best
+ * supported authentication method or a PEAR_Error object
+ * if a failure condition is encountered.
+ * @access private
+ * @since 1.1.0
+ */
+ function _getBestAuthMethod()
+ {
+ $available_methods = explode(' ', $this->_esmtp['AUTH']);
+
+ foreach ($this->auth_methods as $method) {
+ if (in_array($method, $available_methods)) {
+ return $method;
+ }
+ }
+
+ return PEAR::raiseError('No supported authentication methods');
+ }
+
+ /**
+ * Attempt to do SMTP authentication.
+ *
+ * @param string The userid to authenticate as.
+ * @param string The password to authenticate with.
+ * @param string The requested authentication method. If none is
+ * specified, the best supported method will be used.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function auth($uid, $pwd , $method = '')
+ {
+ if (empty($this->_esmtp['AUTH'])) {
+ return PEAR::raiseError('SMTP server does no support authentication');
+ }
+
+ /* If no method has been specified, get the name of the best
+ * supported method advertised by the SMTP server. */
+ if (empty($method)) {
+ if (PEAR::isError($method = $this->_getBestAuthMethod())) {
+ /* Return the PEAR_Error object from _getBestAuthMethod(). */
+ return $method;
+ }
+ } else {
+ $method = strtoupper($method);
+ if (!in_array($method, $this->auth_methods)) {
+ return PEAR::raiseError("$method is not a supported authentication method");
+ }
+ }
+
+ switch ($method) {
+ case 'DIGEST-MD5':
+ $result = $this->_authDigest_MD5($uid, $pwd);
+ break;
+ case 'CRAM-MD5':
+ $result = $this->_authCRAM_MD5($uid, $pwd);
+ break;
+ case 'LOGIN':
+ $result = $this->_authLogin($uid, $pwd);
+ break;
+ case 'PLAIN':
+ $result = $this->_authPlain($uid, $pwd);
+ break;
+ default:
+ $result = PEAR::raiseError("$method is not a supported authentication method");
+ break;
+ }
+
+ /* If an error was encountered, return the PEAR_Error object. */
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Authenticates the user using the DIGEST-MD5 method.
+ *
+ * @param string The userid to authenticate as.
+ * @param string The password to authenticate with.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authDigest_MD5($uid, $pwd)
+ {
+ if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
+ return $error;
+ }
+ /* 334: Continue authentication request */
+ if (PEAR::isError($error = $this->_parseResponse(334))) {
+ /* 503: Error: already authenticated */
+ if ($this->_code === 503) {
+ return true;
+ }
+ return $error;
+ }
+
+ $challenge = base64_decode($this->_arguments[0]);
+ $digest = &Auth_SASL::factory('digestmd5');
+ $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
+ $this->host, "smtp"));
+
+ if (PEAR::isError($error = $this->_put($auth_str))) {
+ return $error;
+ }
+ /* 334: Continue authentication request */
+ if (PEAR::isError($error = $this->_parseResponse(334))) {
+ return $error;
+ }
+
+ /* We don't use the protocol's third step because SMTP doesn't
+ * allow subsequent authentication, so we just silently ignore
+ * it. */
+ if (PEAR::isError($error = $this->_put(' '))) {
+ return $error;
+ }
+ /* 235: Authentication successful */
+ if (PEAR::isError($error = $this->_parseResponse(235))) {
+ return $error;
+ }
+ }
+
+ /**
+ * Authenticates the user using the CRAM-MD5 method.
+ *
+ * @param string The userid to authenticate as.
+ * @param string The password to authenticate with.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authCRAM_MD5($uid, $pwd)
+ {
+ if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
+ return $error;
+ }
+ /* 334: Continue authentication request */
+ if (PEAR::isError($error = $this->_parseResponse(334))) {
+ /* 503: Error: already authenticated */
+ if ($this->_code === 503) {
+ return true;
+ }
+ return $error;
+ }
+
+ $challenge = base64_decode($this->_arguments[0]);
+ $cram = &Auth_SASL::factory('crammd5');
+ $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
+
+ if (PEAR::isError($error = $this->_put($auth_str))) {
+ return $error;
+ }
+
+ /* 235: Authentication successful */
+ if (PEAR::isError($error = $this->_parseResponse(235))) {
+ return $error;
+ }
+ }
+
+ /**
+ * Authenticates the user using the LOGIN method.
+ *
+ * @param string The userid to authenticate as.
+ * @param string The password to authenticate with.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authLogin($uid, $pwd)
+ {
+ if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
+ return $error;
+ }
+ /* 334: Continue authentication request */
+ if (PEAR::isError($error = $this->_parseResponse(334))) {
+ /* 503: Error: already authenticated */
+ if ($this->_code === 503) {
+ return true;
+ }
+ return $error;
+ }
+
+ if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
+ return $error;
+ }
+ /* 334: Continue authentication request */
+ if (PEAR::isError($error = $this->_parseResponse(334))) {
+ return $error;
+ }
+
+ if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
+ return $error;
+ }
+
+ /* 235: Authentication successful */
+ if (PEAR::isError($error = $this->_parseResponse(235))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Authenticates the user using the PLAIN method.
+ *
+ * @param string The userid to authenticate as.
+ * @param string The password to authenticate with.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authPlain($uid, $pwd)
+ {
+ if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
+ return $error;
+ }
+ /* 334: Continue authentication request */
+ if (PEAR::isError($error = $this->_parseResponse(334))) {
+ /* 503: Error: already authenticated */
+ if ($this->_code === 503) {
+ return true;
+ }
+ return $error;
+ }
+
+ $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
+
+ if (PEAR::isError($error = $this->_put($auth_str))) {
+ return $error;
+ }
+
+ /* 235: Authentication successful */
+ if (PEAR::isError($error = $this->_parseResponse(235))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the HELO command.
+ *
+ * @param string The domain name to say we are.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function helo($domain)
+ {
+ if (PEAR::isError($error = $this->_put('HELO', $domain))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the MAIL FROM: command.
+ *
+ * @param string The sender (reverse path) to set.
+ *
+ * @param array optional arguments. Currently supported:
+ * verp boolean or string. If true or string
+ * verp is enabled. If string the characters
+ * are considered verp separators.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function mailFrom($sender, $args = array())
+ {
+ $argstr = '';
+
+ if (isset($args['verp'])) {
+ /* XVERP */
+ if ($args['verp'] === true) {
+ $argstr .= ' XVERP';
+
+ /* XVERP=something */
+ } elseif (trim($args['verp'])) {
+ $argstr .= ' XVERP=' . $args['verp'];
+ }
+ }
+
+ if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>$argstr"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the RCPT TO: command.
+ *
+ * @param string The recipient (forward path) to add.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function rcptTo($recipient)
+ {
+ if (PEAR::isError($error = $this->_put('RCPT', "TO:<$recipient>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(array(250, 251)))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Quote the data so that it meets SMTP standards.
+ *
+ * This is provided as a separate public function to facilitate
+ * easier overloading for the cases where it is desirable to
+ * customize the quoting behavior.
+ *
+ * @param string $data The message text to quote. The string must be passed
+ * by reference, and the text will be modified in place.
+ *
+ * @access public
+ * @since 1.2
+ */
+ function quotedata(&$data)
+ {
+ /* Change Unix (\n) and Mac (\r) linefeeds into
+ * Internet-standard CRLF (\r\n) linefeeds. */
+ $data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
+
+ /* Because a single leading period (.) signifies an end to the
+ * data, legitimate leading periods need to be "doubled"
+ * (e.g. '..'). */
+ $data = str_replace("\n.", "\n..", $data);
+ }
+
+ /**
+ * Send the DATA command.
+ *
+ * @param string $data The message body to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function data($data)
+ {
+ /* RFC 1870, section 3, subsection 3 states "a value of zero
+ * indicates that no fixed maximum message size is in force".
+ * Furthermore, it says that if "the parameter is omitted no
+ * information is conveyed about the server's fixed maximum
+ * message size". */
+ if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
+ if (strlen($data) >= $this->_esmtp['SIZE']) {
+ $this->disconnect();
+ return PEAR::raiseError('Message size excedes the server limit');
+ }
+ }
+
+ /* Quote the data based on the SMTP standards. */
+ $this->quotedata($data);
+
+ if (PEAR::isError($error = $this->_put('DATA'))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(354))) {
+ return $error;
+ }
+
+ if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) {
+ return $result;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the SEND FROM: command.
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.2.6
+ */
+ function sendFrom($path)
+ {
+ if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility wrapper for sendFrom().
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access public
+ * @since 1.0
+ * @deprecated 1.2.6
+ */
+ function send_from($path)
+ {
+ return sendFrom($path);
+ }
+
+ /**
+ * Send the SOML FROM: command.
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.2.6
+ */
+ function somlFrom($path)
+ {
+ if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility wrapper for somlFrom().
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access public
+ * @since 1.0
+ * @deprecated 1.2.6
+ */
+ function soml_from($path)
+ {
+ return somlFrom($path);
+ }
+
+ /**
+ * Send the SAML FROM: command.
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.2.6
+ */
+ function samlFrom($path)
+ {
+ if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility wrapper for samlFrom().
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access public
+ * @since 1.0
+ * @deprecated 1.2.6
+ */
+ function saml_from($path)
+ {
+ return samlFrom($path);
+ }
+
+ /**
+ * Send the RSET command.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function rset()
+ {
+ if (PEAR::isError($error = $this->_put('RSET'))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the VRFY command.
+ *
+ * @param string The string to verify
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function vrfy($string)
+ {
+ /* Note: 251 is also a valid response code */
+ if (PEAR::isError($error = $this->_put('VRFY', $string))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the NOOP command.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function noop()
+ {
+ if (PEAR::isError($error = $this->_put('NOOP'))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility method. identifySender()'s functionality is
+ * now handled internally.
+ *
+ * @return boolean This method always return true.
+ *
+ * @access public
+ * @since 1.0
+ */
+ function identifySender()
+ {
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Chuck Hagenbuch <chuck@horde.org> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Socket.php 17 2005-10-03 20:25:31Z roundcube $
+//
+
+require_once 'PEAR.php';
+
+/**
+ * Generalized Socket class. More docs to be written.
+ *
+ * @version 1.0
+ * @author Stig Bakken <ssb@php.net>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+class Net_Socket extends PEAR {
+ // {{{ properties
+
+ /** Socket file pointer. */
+ var $fp = null;
+
+ /** Whether the socket is blocking. */
+ var $blocking = true;
+
+ /** Whether the socket is persistent. */
+ var $persistent = false;
+
+ /** The IP address to connect to. */
+ var $addr = '';
+
+ /** The port number to connect to. */
+ var $port = 0;
+
+ /** Number of seconds to wait on socket connections before
+ assuming there's no more data. */
+ var $timeout = false;
+
+ /** Number of bytes to read at a time in readLine() and
+ readAll(). */
+ var $lineLength = 2048;
+ // }}}
+
+ // {{{ constructor
+ /**
+ * Constructs a new Net_Socket object.
+ *
+ * @access public
+ */
+ function Net_Socket()
+ {
+ $this->PEAR();
+ }
+ // }}}
+
+ // {{{ connect()
+ /**
+ * Connect to the specified port. If called when the socket is
+ * already connected, it disconnects and connects again.
+ *
+ * @param $addr string IP address or host name
+ * @param $port int TCP port number
+ * @param $persistent bool (optional) whether the connection is
+ * persistent (kept open between requests by the web server)
+ * @param $timeout int (optional) how long to wait for data
+ * @param $options array see options for stream_context_create
+ * @access public
+ * @return mixed true on success or error object
+ */
+ function connect($addr, $port, $persistent = null, $timeout = null, $options = null)
+ {
+ if (is_resource($this->fp)) {
+ @fclose($this->fp);
+ $this->fp = null;
+ }
+
+ if (strspn($addr, '.0123456789') == strlen($addr)) {
+ $this->addr = $addr;
+ } else {
+ $this->addr = gethostbyname($addr);
+ }
+ $this->port = $port % 65536;
+ if ($persistent !== null) {
+ $this->persistent = $persistent;
+ }
+ if ($timeout !== null) {
+ $this->timeout = $timeout;
+ }
+ $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
+ $errno = 0;
+ $errstr = '';
+ if ($options && function_exists('stream_context_create')) {
+ if ($this->timeout) {
+ $timeout = $this->timeout;
+ } else {
+ $timeout = 0;
+ }
+ $context = stream_context_create($options);
+ $fp = $openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
+ } else {
+ if ($this->timeout) {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
+ } else {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
+ }
+ }
+
+ if (!$fp) {
+ return $this->raiseError($errstr, $errno);
+ }
+
+ $this->fp = $fp;
+
+ return $this->setBlocking($this->blocking);
+ }
+ // }}}
+
+ // {{{ disconnect()
+ /**
+ * Disconnects from the peer, closes the socket.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function disconnect()
+ {
+ if (is_resource($this->fp)) {
+ fclose($this->fp);
+ $this->fp = null;
+ return true;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ isBlocking()
+ /**
+ * Find out if the socket is in blocking mode.
+ *
+ * @access public
+ * @return bool the current blocking mode.
+ */
+ function isBlocking()
+ {
+ return $this->blocking;
+ }
+ // }}}
+
+ // {{{ setBlocking()
+ /**
+ * Sets whether the socket connection should be blocking or
+ * not. A read call to a non-blocking socket will return immediately
+ * if there is no data available, whereas it will block until there
+ * is data for blocking sockets.
+ *
+ * @param $mode bool true for blocking sockets, false for nonblocking
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setBlocking($mode)
+ {
+ if (is_resource($this->fp)) {
+ $this->blocking = $mode;
+ socket_set_blocking($this->fp, $this->blocking);
+ return true;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ setTimeout()
+ /**
+ * Sets the timeout value on socket descriptor,
+ * expressed in the sum of seconds and microseconds
+ *
+ * @param $seconds int seconds
+ * @param $microseconds int microseconds
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setTimeout($seconds, $microseconds)
+ {
+ if (is_resource($this->fp)) {
+ socket_set_timeout($this->fp, $seconds, $microseconds);
+ return true;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ getStatus()
+ /**
+ * Returns information about an existing socket resource.
+ * Currently returns four entries in the result array:
+ *
+ * <p>
+ * timed_out (bool) - The socket timed out waiting for data<br>
+ * blocked (bool) - The socket was blocked<br>
+ * eof (bool) - Indicates EOF event<br>
+ * unread_bytes (int) - Number of bytes left in the socket buffer<br>
+ * </p>
+ *
+ * @access public
+ * @return mixed Array containing information about existing socket resource or an error object otherwise
+ */
+ function getStatus()
+ {
+ if (is_resource($this->fp)) {
+ return socket_get_status($this->fp);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ gets()
+ /**
+ * Get a specified line of data
+ *
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function gets($size)
+ {
+ if (is_resource($this->fp)) {
+ return fgets($this->fp, $size);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ read()
+ /**
+ * Read a specified amount of data. This is guaranteed to return,
+ * and has the added benefit of getting everything in one fread()
+ * chunk; if you know the size of the data you're getting
+ * beforehand, this is definitely the way to go.
+ *
+ * @param $size The number of bytes to read from the socket.
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function read($size)
+ {
+ if (is_resource($this->fp)) {
+ return fread($this->fp, $size);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ write()
+ /**
+ * Write a specified amount of data.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function write($data)
+ {
+ if (is_resource($this->fp)) {
+ return fwrite($this->fp, $data);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ writeLine()
+ /**
+ * Write a line of data to the socket, followed by a trailing "\r\n".
+ *
+ * @access public
+ * @return mixed fputs result, or an error
+ */
+ function writeLine ($data)
+ {
+ if (is_resource($this->fp)) {
+ return $this->write($data . "\r\n");
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ eof()
+ /**
+ * Tests for end-of-file on a socket descriptor
+ *
+ * @access public
+ * @return bool
+ */
+ function eof()
+ {
+ return (is_resource($this->fp) && feof($this->fp));
+ }
+ // }}}
+
+ // {{{ readByte()
+ /**
+ * Reads a byte of data
+ *
+ * @access public
+ * @return 1 byte of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readByte()
+ {
+ if (is_resource($this->fp)) {
+ return ord($this->read(1));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readWord()
+ /**
+ * Reads a word of data
+ *
+ * @access public
+ * @return 1 word of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readWord()
+ {
+ if (is_resource($this->fp)) {
+ $buf = $this->read(2);
+ return (ord($buf[0]) + (ord($buf[1]) << 8));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readInt()
+ /**
+ * Reads an int of data
+ *
+ * @access public
+ * @return 1 int of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readInt()
+ {
+ if (is_resource($this->fp)) {
+ $buf = $this->read(4);
+ return (ord($buf[0]) + (ord($buf[1]) << 8) +
+ (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readString()
+ /**
+ * Reads a zeroterminated string of data
+ *
+ * @access public
+ * @return string, or a PEAR_Error if
+ * not connected.
+ */
+ function readString()
+ {
+ if (is_resource($this->fp)) {
+ $string = '';
+ while (($char = $this->read(1)) != "\x00") {
+ $string .= $char;
+ }
+ return $string;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readIPAddress()
+ /**
+ * Reads an IP Address and returns it in a dot formated string
+ *
+ * @access public
+ * @return Dot formated string, or a PEAR_Error if
+ * not connected.
+ */
+ function readIPAddress()
+ {
+ if (is_resource($this->fp)) {
+ $buf = $this->read(4);
+ return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
+ ord($buf[2]), ord($buf[3]));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readLine()
+ /**
+ * Read until either the end of the socket or a newline, whichever
+ * comes first. Strips the trailing newline from the returned data.
+ *
+ * @access public
+ * @return All available data up to a newline, without that
+ * newline, or until the end of the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readLine()
+ {
+ if (is_resource($this->fp)) {
+ $line = '';
+ $timeout = time() + $this->timeout;
+ while (!$this->eof() && (!$this->timeout || time() < $timeout)) {
+ $line .= $this->gets($this->lineLength);
+ if (substr($line, -2) == "\r\n" ||
+ substr($line, -1) == "\n") {
+ return rtrim($line, "\r\n");
+ }
+ }
+ return $line;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readAll()
+ /**
+ * Read until the socket closes. THIS FUNCTION WILL NOT EXIT if the
+ * socket is in blocking mode until the socket closes.
+ *
+ * @access public
+ * @return All data until the socket closes, or a PEAR_Error if
+ * not connected.
+ */
+ function readAll()
+ {
+ if (is_resource($this->fp)) {
+ $data = '';
+ while (!$this->eof())
+ $data .= $this->read($this->lineLength);
+ return $data;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+}
--- /dev/null
+<?php
+/**
+ * PEAR, the PHP Extension and Application Repository
+ *
+ * PEAR class and PEAR_Error class
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category pear
+ * @package PEAR
+ * @author Sterling Hughes <sterling@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Greg Beaver <cellog@php.net>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: PEAR.php 157 2006-03-03 16:15:07Z roundcube $
+ * @link http://pear.php.net/package/PEAR
+ * @since File available since Release 0.1
+ */
+
+/**#@+
+ * ERROR constants
+ */
+define('PEAR_ERROR_RETURN', 1);
+define('PEAR_ERROR_PRINT', 2);
+define('PEAR_ERROR_TRIGGER', 4);
+define('PEAR_ERROR_DIE', 8);
+define('PEAR_ERROR_CALLBACK', 16);
+/**
+ * WARNING: obsolete
+ * @deprecated
+ */
+define('PEAR_ERROR_EXCEPTION', 32);
+/**#@-*/
+define('PEAR_ZE2', (function_exists('version_compare') &&
+ version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ define('OS_WINDOWS', true);
+ define('OS_UNIX', false);
+ define('PEAR_OS', 'Windows');
+} else {
+ define('OS_WINDOWS', false);
+ define('OS_UNIX', true);
+ define('PEAR_OS', 'Unix'); // blatant assumption
+}
+
+// instant backwards compatibility
+if (!defined('PATH_SEPARATOR')) {
+ if (OS_WINDOWS) {
+ define('PATH_SEPARATOR', ';');
+ } else {
+ define('PATH_SEPARATOR', ':');
+ }
+}
+
+$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs'] = array();
+$GLOBALS['_PEAR_error_handler_stack'] = array();
+
+@ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes. Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix). Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters. Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded. If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference: $obj =& new PEAR_child;
+ *
+ * @category pear
+ * @package PEAR
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Greg Beaver <cellog@php.net>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.4.7
+ * @link http://pear.php.net/package/PEAR
+ * @see PEAR_Error
+ * @since Class available since PHP 4.0.2
+ * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
+ */
+class PEAR
+{
+ // {{{ properties
+
+ /**
+ * Whether to enable internal debug messages.
+ *
+ * @var bool
+ * @access private
+ */
+ var $_debug = false;
+
+ /**
+ * Default error mode for this object.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_mode = null;
+
+ /**
+ * Default error options used for this object when error mode
+ * is PEAR_ERROR_TRIGGER.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_options = null;
+
+ /**
+ * Default error handler (callback) for this object, if error mode is
+ * PEAR_ERROR_CALLBACK.
+ *
+ * @var string
+ * @access private
+ */
+ var $_default_error_handler = '';
+
+ /**
+ * Which class to use for error objects.
+ *
+ * @var string
+ * @access private
+ */
+ var $_error_class = 'PEAR_Error';
+
+ /**
+ * An array of expected errors.
+ *
+ * @var array
+ * @access private
+ */
+ var $_expected_errors = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * Constructor. Registers this object in
+ * $_PEAR_destructor_object_list for destructor emulation if a
+ * destructor object exists.
+ *
+ * @param string $error_class (optional) which class to use for
+ * error objects, defaults to PEAR_Error.
+ * @access public
+ * @return void
+ */
+ function PEAR($error_class = null)
+ {
+ $classname = strtolower(get_class($this));
+ if ($this->_debug) {
+ print "PEAR constructor called, class=$classname\n";
+ }
+ if ($error_class !== null) {
+ $this->_error_class = $error_class;
+ }
+ while ($classname && strcasecmp($classname, "pear")) {
+ $destructor = "_$classname";
+ if (method_exists($this, $destructor)) {
+ global $_PEAR_destructor_object_list;
+ $_PEAR_destructor_object_list[] = &$this;
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+ register_shutdown_function("_PEAR_call_destructors");
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+ }
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * Destructor (the emulated type of...). Does nothing right now,
+ * but is included for forward compatibility, so subclass
+ * destructors should always call it.
+ *
+ * See the note in the class desciption about output from
+ * destructors.
+ *
+ * @access public
+ * @return void
+ */
+ function _PEAR() {
+ if ($this->_debug) {
+ printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
+ }
+ }
+
+ // }}}
+ // {{{ getStaticProperty()
+
+ /**
+ * If you have a class that's mostly/entirely static, and you need static
+ * properties, you can use this method to simulate them. Eg. in your method(s)
+ * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
+ * You MUST use a reference, or they will not persist!
+ *
+ * @access public
+ * @param string $class The calling classname, to prevent clashes
+ * @param string $var The variable to retrieve.
+ * @return mixed A reference to the variable. If not set it will be
+ * auto initialised to NULL.
+ */
+ function &getStaticProperty($class, $var)
+ {
+ static $properties;
+ return $properties[$class][$var];
+ }
+
+ // }}}
+ // {{{ registerShutdownFunc()
+
+ /**
+ * Use this function to register a shutdown method for static
+ * classes.
+ *
+ * @access public
+ * @param mixed $func The function name (or array of class/method) to call
+ * @param mixed $args The arguments to pass to the function
+ * @return void
+ */
+ function registerShutdownFunc($func, $args = array())
+ {
+ // if we are called statically, there is a potential
+ // that no shutdown func is registered. Bug #6445
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+ register_shutdown_function("_PEAR_call_destructors");
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+ }
+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+ }
+
+ // }}}
+ // {{{ isError()
+
+ /**
+ * Tell whether a value is a PEAR error.
+ *
+ * @param mixed $data the value to test
+ * @param int $code if $data is an error object, return true
+ * only if $code is a string and
+ * $obj->getMessage() == $code or
+ * $code is an integer and $obj->getCode() == $code
+ * @access public
+ * @return bool true if parameter is an error
+ */
+ function isError($data, $code = null)
+ {
+ if (is_a($data, 'PEAR_Error')) {
+ if (is_null($code)) {
+ return true;
+ } elseif (is_string($code)) {
+ return $data->getMessage() == $code;
+ } else {
+ return $data->getCode() == $code;
+ }
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ setErrorHandling()
+
+ /**
+ * Sets how errors generated by this object should be handled.
+ * Can be invoked both in objects and statically. If called
+ * statically, setErrorHandling sets the default behaviour for all
+ * PEAR objects. If called in an object, setErrorHandling sets
+ * the default behaviour for that object.
+ *
+ * @param int $mode
+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
+ *
+ * @param mixed $options
+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ *
+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+ * to be the callback function or method. A callback
+ * function is a string with the name of the function, a
+ * callback method is an array of two elements: the element
+ * at index 0 is the object, and the element at index 1 is
+ * the name of the method to call in the object.
+ *
+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+ * a printf format string used when printing the error
+ * message.
+ *
+ * @access public
+ * @return void
+ * @see PEAR_ERROR_RETURN
+ * @see PEAR_ERROR_PRINT
+ * @see PEAR_ERROR_TRIGGER
+ * @see PEAR_ERROR_DIE
+ * @see PEAR_ERROR_CALLBACK
+ * @see PEAR_ERROR_EXCEPTION
+ *
+ * @since PHP 4.0.5
+ */
+
+ function setErrorHandling($mode = null, $options = null)
+ {
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $setmode = &$this->_default_error_mode;
+ $setoptions = &$this->_default_error_options;
+ } else {
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ }
+
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ }
+
+ // }}}
+ // {{{ expectError()
+
+ /**
+ * This method is used to tell which errors you expect to get.
+ * Expected errors are always returned with error mode
+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
+ * and this method pushes a new element onto it. The list of
+ * expected errors are in effect until they are popped off the
+ * stack with the popExpect() method.
+ *
+ * Note that this method can not be called statically
+ *
+ * @param mixed $code a single error code or an array of error codes to expect
+ *
+ * @return int the new depth of the "expected errors" stack
+ * @access public
+ */
+ function expectError($code = '*')
+ {
+ if (is_array($code)) {
+ array_push($this->_expected_errors, $code);
+ } else {
+ array_push($this->_expected_errors, array($code));
+ }
+ return sizeof($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ popExpect()
+
+ /**
+ * This method pops one element off the expected error codes
+ * stack.
+ *
+ * @return array the list of error codes that were popped
+ */
+ function popExpect()
+ {
+ return array_pop($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ _checkDelExpect()
+
+ /**
+ * This method checks unsets an error code if available
+ *
+ * @param mixed error code
+ * @return bool true if the error code was unset, false otherwise
+ * @access private
+ * @since PHP 4.3.0
+ */
+ function _checkDelExpect($error_code)
+ {
+ $deleted = false;
+
+ foreach ($this->_expected_errors AS $key => $error_array) {
+ if (in_array($error_code, $error_array)) {
+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+ $deleted = true;
+ }
+
+ // clean up empty arrays
+ if (0 == count($this->_expected_errors[$key])) {
+ unset($this->_expected_errors[$key]);
+ }
+ }
+ return $deleted;
+ }
+
+ // }}}
+ // {{{ delExpect()
+
+ /**
+ * This method deletes all occurences of the specified element from
+ * the expected error codes stack.
+ *
+ * @param mixed $error_code error code that should be deleted
+ * @return mixed list of error codes that were deleted or error
+ * @access public
+ * @since PHP 4.3.0
+ */
+ function delExpect($error_code)
+ {
+ $deleted = false;
+
+ if ((is_array($error_code) && (0 != count($error_code)))) {
+ // $error_code is a non-empty array here;
+ // we walk through it trying to unset all
+ // values
+ foreach($error_code as $key => $error) {
+ if ($this->_checkDelExpect($error)) {
+ $deleted = true;
+ } else {
+ $deleted = false;
+ }
+ }
+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ } elseif (!empty($error_code)) {
+ // $error_code comes alone, trying to unset it
+ if ($this->_checkDelExpect($error_code)) {
+ return true;
+ } else {
+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ }
+ } else {
+ // $error_code is empty
+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+ }
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * This method is a wrapper that returns an instance of the
+ * configured error class with this object's default error
+ * handling applied. If the $mode and $options parameters are not
+ * specified, the object's defaults are used.
+ *
+ * @param mixed $message a text error message or a PEAR error object
+ *
+ * @param int $code a numeric error code (it is up to your class
+ * to define these if you want to use codes)
+ *
+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+ * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
+ *
+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+ * specifies the PHP-internal error level (one of
+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ * If $mode is PEAR_ERROR_CALLBACK, this
+ * parameter specifies the callback function or
+ * method. In other error modes this parameter
+ * is ignored.
+ *
+ * @param string $userinfo If you need to pass along for example debug
+ * information, this parameter is meant for that.
+ *
+ * @param string $error_class The returned error object will be
+ * instantiated from this class, if specified.
+ *
+ * @param bool $skipmsg If true, raiseError will only pass error codes,
+ * the error message parameter will be dropped.
+ *
+ * @access public
+ * @return object a PEAR error object
+ * @see PEAR::setErrorHandling
+ * @since PHP 4.0.5
+ */
+ function &raiseError($message = null,
+ $code = null,
+ $mode = null,
+ $options = null,
+ $userinfo = null,
+ $error_class = null,
+ $skipmsg = false)
+ {
+ // The error is yet a PEAR error object
+ if (is_object($message)) {
+ $code = $message->getCode();
+ $userinfo = $message->getUserInfo();
+ $error_class = $message->getType();
+ $message->error_message_prefix = '';
+ $message = $message->getMessage();
+ }
+
+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
+ if ($exp[0] == "*" ||
+ (is_int(reset($exp)) && in_array($code, $exp)) ||
+ (is_string(reset($exp)) && in_array($message, $exp))) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ }
+ // No mode given, try global ones
+ if ($mode === null) {
+ // Class error handler
+ if (isset($this) && isset($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ // Global error handler
+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+ $mode = $GLOBALS['_PEAR_default_error_mode'];
+ $options = $GLOBALS['_PEAR_default_error_options'];
+ }
+ }
+
+ if ($error_class !== null) {
+ $ec = $error_class;
+ } elseif (isset($this) && isset($this->_error_class)) {
+ $ec = $this->_error_class;
+ } else {
+ $ec = 'PEAR_Error';
+ }
+ if ($skipmsg) {
+ $a = &new $ec($code, $mode, $options, $userinfo);
+ return $a;
+ } else {
+ $a = &new $ec($message, $code, $mode, $options, $userinfo);
+ return $a;
+ }
+ }
+
+ // }}}
+ // {{{ throwError()
+
+ /**
+ * Simpler form of raiseError with fewer options. In most cases
+ * message, code and userinfo are enough.
+ *
+ * @param string $message
+ *
+ */
+ function &throwError($message = null,
+ $code = null,
+ $userinfo = null)
+ {
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $a = &$this->raiseError($message, $code, null, null, $userinfo);
+ return $a;
+ } else {
+ $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
+ return $a;
+ }
+ }
+
+ // }}}
+ function staticPushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ $stack[] = array($def_mode, $def_options);
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $def_mode = $mode;
+ $def_options = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $def_mode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $def_options = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ function staticPopErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ return true;
+ }
+
+ // {{{ pushErrorHandling()
+
+ /**
+ * Push a new error handler on top of the error handler options stack. With this
+ * you can easily override the actual error handler for some code and restore
+ * it later with popErrorHandling.
+ *
+ * @param mixed $mode (same as setErrorHandling)
+ * @param mixed $options (same as setErrorHandling)
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::setErrorHandling
+ */
+ function pushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $def_mode = &$this->_default_error_mode;
+ $def_options = &$this->_default_error_options;
+ } else {
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ }
+ $stack[] = array($def_mode, $def_options);
+
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ // }}}
+ // {{{ popErrorHandling()
+
+ /**
+ * Pop the last error handler used
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::pushErrorHandling
+ */
+ function popErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ loadExtension()
+
+ /**
+ * OS independant PHP extension load. Remember to take care
+ * on the correct extension name for case sensitive OSes.
+ *
+ * @param string $ext The extension name
+ * @return bool Success or not on the dl() call
+ */
+ function loadExtension($ext)
+ {
+ if (!extension_loaded($ext)) {
+ // if either returns true dl() will produce a FATAL error, stop that
+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
+ return false;
+ }
+ if (OS_WINDOWS) {
+ $suffix = '.dll';
+ } elseif (PHP_OS == 'HP-UX') {
+ $suffix = '.sl';
+ } elseif (PHP_OS == 'AIX') {
+ $suffix = '.a';
+ } elseif (PHP_OS == 'OSX') {
+ $suffix = '.bundle';
+ } else {
+ $suffix = '.so';
+ }
+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+ }
+ return true;
+ }
+
+ // }}}
+}
+
+// {{{ _PEAR_call_destructors()
+
+function _PEAR_call_destructors()
+{
+ global $_PEAR_destructor_object_list;
+ if (is_array($_PEAR_destructor_object_list) &&
+ sizeof($_PEAR_destructor_object_list))
+ {
+ reset($_PEAR_destructor_object_list);
+ if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
+ $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
+ }
+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+ $classname = get_class($objref);
+ while ($classname) {
+ $destructor = "_$classname";
+ if (method_exists($objref, $destructor)) {
+ $objref->$destructor();
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+ // Empty the object list to ensure that destructors are
+ // not called more than once.
+ $_PEAR_destructor_object_list = array();
+ }
+
+ // Now call the shutdown functions
+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+ call_user_func_array($value[0], $value[1]);
+ }
+ }
+}
+
+// }}}
+/**
+ * Standard PEAR error class for PHP 4
+ *
+ * This class is supserseded by {@link PEAR_Exception} in PHP 5
+ *
+ * @category pear
+ * @package PEAR
+ * @author Stig Bakken <ssb@php.net>
+ * @author Tomas V.V. Cox <cox@idecnet.com>
+ * @author Gregory Beaver <cellog@php.net>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.4.7
+ * @link http://pear.php.net/manual/en/core.pear.pear-error.php
+ * @see PEAR::raiseError(), PEAR::throwError()
+ * @since Class available since PHP 4.0.2
+ */
+class PEAR_Error
+{
+ // {{{ properties
+
+ var $error_message_prefix = '';
+ var $mode = PEAR_ERROR_RETURN;
+ var $level = E_USER_NOTICE;
+ var $code = -1;
+ var $message = '';
+ var $userinfo = '';
+ var $backtrace = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Error constructor
+ *
+ * @param string $message message
+ *
+ * @param int $code (optional) error code
+ *
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+ *
+ * @param mixed $options (optional) error level, _OR_ in the case of
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
+ * tuple.
+ *
+ * @param string $userinfo (optional) additional user/debug info
+ *
+ * @access public
+ *
+ */
+ function PEAR_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+ if ($mode === null) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ $this->message = $message;
+ $this->code = $code;
+ $this->mode = $mode;
+ $this->userinfo = $userinfo;
+ if (function_exists("debug_backtrace")) {
+ if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
+ $this->backtrace = debug_backtrace();
+ }
+ }
+ if ($mode & PEAR_ERROR_CALLBACK) {
+ $this->level = E_USER_NOTICE;
+ $this->callback = $options;
+ } else {
+ if ($options === null) {
+ $options = E_USER_NOTICE;
+ }
+ $this->level = $options;
+ $this->callback = null;
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ } else {
+ $format = $options;
+ }
+ printf($format, $this->getMessage());
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ trigger_error($this->getMessage(), $this->level);
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $msg = $this->getMessage();
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ if (substr($msg, -1) != "\n") {
+ $msg .= "\n";
+ }
+ } else {
+ $format = $options;
+ }
+ die(sprintf($format, $msg));
+ }
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_callable($this->callback)) {
+ call_user_func($this->callback, $this);
+ }
+ }
+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
+ eval('$e = new Exception($this->message, $this->code);throw($e);');
+ }
+ }
+
+ // }}}
+ // {{{ getMode()
+
+ /**
+ * Get the error mode from an error object.
+ *
+ * @return int error mode
+ * @access public
+ */
+ function getMode() {
+ return $this->mode;
+ }
+
+ // }}}
+ // {{{ getCallback()
+
+ /**
+ * Get the callback function/method from an error object.
+ *
+ * @return mixed callback function or object/method array
+ * @access public
+ */
+ function getCallback() {
+ return $this->callback;
+ }
+
+ // }}}
+ // {{{ getMessage()
+
+
+ /**
+ * Get the error message from an error object.
+ *
+ * @return string full error message
+ * @access public
+ */
+ function getMessage()
+ {
+ return ($this->error_message_prefix . $this->message);
+ }
+
+
+ // }}}
+ // {{{ getCode()
+
+ /**
+ * Get error code from an error object
+ *
+ * @return int error code
+ * @access public
+ */
+ function getCode()
+ {
+ return $this->code;
+ }
+
+ // }}}
+ // {{{ getType()
+
+ /**
+ * Get the name of this error/exception.
+ *
+ * @return string error/exception name (type)
+ * @access public
+ */
+ function getType()
+ {
+ return get_class($this);
+ }
+
+ // }}}
+ // {{{ getUserInfo()
+
+ /**
+ * Get additional user-supplied information.
+ *
+ * @return string user-supplied information
+ * @access public
+ */
+ function getUserInfo()
+ {
+ return $this->userinfo;
+ }
+
+ // }}}
+ // {{{ getDebugInfo()
+
+ /**
+ * Get additional debug information supplied by the application.
+ *
+ * @return string debug information
+ * @access public
+ */
+ function getDebugInfo()
+ {
+ return $this->getUserInfo();
+ }
+
+ // }}}
+ // {{{ getBacktrace()
+
+ /**
+ * Get the call backtrace from where the error was generated.
+ * Supported with PHP 4.3.0 or newer.
+ *
+ * @param int $frame (optional) what frame to fetch
+ * @return array Backtrace, or NULL if not available.
+ * @access public
+ */
+ function getBacktrace($frame = null)
+ {
+ if (defined('PEAR_IGNORE_BACKTRACE')) {
+ return null;
+ }
+ if ($frame === null) {
+ return $this->backtrace;
+ }
+ return $this->backtrace[$frame];
+ }
+
+ // }}}
+ // {{{ addUserInfo()
+
+ function addUserInfo($info)
+ {
+ if (empty($this->userinfo)) {
+ $this->userinfo = $info;
+ } else {
+ $this->userinfo .= " ** $info";
+ }
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Make a string representation of this object.
+ *
+ * @return string a string with an object summary
+ * @access public
+ */
+ function toString() {
+ $modes = array();
+ $levels = array(E_USER_NOTICE => 'notice',
+ E_USER_WARNING => 'warning',
+ E_USER_ERROR => 'error');
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_array($this->callback)) {
+ $callback = (is_object($this->callback[0]) ?
+ strtolower(get_class($this->callback[0])) :
+ $this->callback[0]) . '::' .
+ $this->callback[1];
+ } else {
+ $callback = $this->callback;
+ }
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
+ 'callback=%s prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ $callback, $this->error_message_prefix,
+ $this->userinfo);
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ $modes[] = 'print';
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ $modes[] = 'trigger';
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $modes[] = 'die';
+ }
+ if ($this->mode & PEAR_ERROR_RETURN) {
+ $modes[] = 'return';
+ }
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+ 'prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ implode("|", $modes), $levels[$this->level],
+ $this->error_message_prefix,
+ $this->userinfo);
+ }
+
+ // }}}
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
--- /dev/null
+#
+# Name: cp1250 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1250 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1250 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 #UNDEFINED
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 #UNDEFINED
+0x89 0x2030 #PER MILLE SIGN
+0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C 0x015A #LATIN CAPITAL LETTER S WITH ACUTE
+0x8D 0x0164 #LATIN CAPITAL LETTER T WITH CARON
+0x8E 0x017D #LATIN CAPITAL LETTER Z WITH CARON
+0x8F 0x0179 #LATIN CAPITAL LETTER Z WITH ACUTE
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 #UNDEFINED
+0x99 0x2122 #TRADE MARK SIGN
+0x9A 0x0161 #LATIN SMALL LETTER S WITH CARON
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C 0x015B #LATIN SMALL LETTER S WITH ACUTE
+0x9D 0x0165 #LATIN SMALL LETTER T WITH CARON
+0x9E 0x017E #LATIN SMALL LETTER Z WITH CARON
+0x9F 0x017A #LATIN SMALL LETTER Z WITH ACUTE
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x02C7 #CARON
+0xA2 0x02D8 #BREVE
+0xA3 0x0141 #LATIN CAPITAL LETTER L WITH STROKE
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x0104 #LATIN CAPITAL LETTER A WITH OGONEK
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x015E #LATIN CAPITAL LETTER S WITH CEDILLA
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x017B #LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x02DB #OGONEK
+0xB3 0x0142 #LATIN SMALL LETTER L WITH STROKE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00B8 #CEDILLA
+0xB9 0x0105 #LATIN SMALL LETTER A WITH OGONEK
+0xBA 0x015F #LATIN SMALL LETTER S WITH CEDILLA
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x013D #LATIN CAPITAL LETTER L WITH CARON
+0xBD 0x02DD #DOUBLE ACUTE ACCENT
+0xBE 0x013E #LATIN SMALL LETTER L WITH CARON
+0xBF 0x017C #LATIN SMALL LETTER Z WITH DOT ABOVE
+0xC0 0x0154 #LATIN CAPITAL LETTER R WITH ACUTE
+0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x0102 #LATIN CAPITAL LETTER A WITH BREVE
+0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x0139 #LATIN CAPITAL LETTER L WITH ACUTE
+0xC6 0x0106 #LATIN CAPITAL LETTER C WITH ACUTE
+0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x010C #LATIN CAPITAL LETTER C WITH CARON
+0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x0118 #LATIN CAPITAL LETTER E WITH OGONEK
+0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x011A #LATIN CAPITAL LETTER E WITH CARON
+0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x010E #LATIN CAPITAL LETTER D WITH CARON
+0xD0 0x0110 #LATIN CAPITAL LETTER D WITH STROKE
+0xD1 0x0143 #LATIN CAPITAL LETTER N WITH ACUTE
+0xD2 0x0147 #LATIN CAPITAL LETTER N WITH CARON
+0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x0150 #LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 #MULTIPLICATION SIGN
+0xD8 0x0158 #LATIN CAPITAL LETTER R WITH CARON
+0xD9 0x016E #LATIN CAPITAL LETTER U WITH RING ABOVE
+0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x0170 #LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x00DD #LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE 0x0162 #LATIN CAPITAL LETTER T WITH CEDILLA
+0xDF 0x00DF #LATIN SMALL LETTER SHARP S
+0xE0 0x0155 #LATIN SMALL LETTER R WITH ACUTE
+0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x0103 #LATIN SMALL LETTER A WITH BREVE
+0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x013A #LATIN SMALL LETTER L WITH ACUTE
+0xE6 0x0107 #LATIN SMALL LETTER C WITH ACUTE
+0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x010D #LATIN SMALL LETTER C WITH CARON
+0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x0119 #LATIN SMALL LETTER E WITH OGONEK
+0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x011B #LATIN SMALL LETTER E WITH CARON
+0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x010F #LATIN SMALL LETTER D WITH CARON
+0xF0 0x0111 #LATIN SMALL LETTER D WITH STROKE
+0xF1 0x0144 #LATIN SMALL LETTER N WITH ACUTE
+0xF2 0x0148 #LATIN SMALL LETTER N WITH CARON
+0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x0151 #LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 #DIVISION SIGN
+0xF8 0x0159 #LATIN SMALL LETTER R WITH CARON
+0xF9 0x016F #LATIN SMALL LETTER U WITH RING ABOVE
+0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x0171 #LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x00FD #LATIN SMALL LETTER Y WITH ACUTE
+0xFE 0x0163 #LATIN SMALL LETTER T WITH CEDILLA
+0xFF 0x02D9 #DOT ABOVE
--- /dev/null
+#
+# Name: cp1251 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1251 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1251 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x0402 #CYRILLIC CAPITAL LETTER DJE
+0x81 0x0403 #CYRILLIC CAPITAL LETTER GJE
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0453 #CYRILLIC SMALL LETTER GJE
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 0x20AC #EURO SIGN
+0x89 0x2030 #PER MILLE SIGN
+0x8A 0x0409 #CYRILLIC CAPITAL LETTER LJE
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C 0x040A #CYRILLIC CAPITAL LETTER NJE
+0x8D 0x040C #CYRILLIC CAPITAL LETTER KJE
+0x8E 0x040B #CYRILLIC CAPITAL LETTER TSHE
+0x8F 0x040F #CYRILLIC CAPITAL LETTER DZHE
+0x90 0x0452 #CYRILLIC SMALL LETTER DJE
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 #UNDEFINED
+0x99 0x2122 #TRADE MARK SIGN
+0x9A 0x0459 #CYRILLIC SMALL LETTER LJE
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C 0x045A #CYRILLIC SMALL LETTER NJE
+0x9D 0x045C #CYRILLIC SMALL LETTER KJE
+0x9E 0x045B #CYRILLIC SMALL LETTER TSHE
+0x9F 0x045F #CYRILLIC SMALL LETTER DZHE
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x040E #CYRILLIC CAPITAL LETTER SHORT U
+0xA2 0x045E #CYRILLIC SMALL LETTER SHORT U
+0xA3 0x0408 #CYRILLIC CAPITAL LETTER JE
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x0490 #CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x0401 #CYRILLIC CAPITAL LETTER IO
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x0404 #CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x0407 #CYRILLIC CAPITAL LETTER YI
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x0406 #CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xB3 0x0456 #CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xB4 0x0491 #CYRILLIC SMALL LETTER GHE WITH UPTURN
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x0451 #CYRILLIC SMALL LETTER IO
+0xB9 0x2116 #NUMERO SIGN
+0xBA 0x0454 #CYRILLIC SMALL LETTER UKRAINIAN IE
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x0458 #CYRILLIC SMALL LETTER JE
+0xBD 0x0405 #CYRILLIC CAPITAL LETTER DZE
+0xBE 0x0455 #CYRILLIC SMALL LETTER DZE
+0xBF 0x0457 #CYRILLIC SMALL LETTER YI
+0xC0 0x0410 #CYRILLIC CAPITAL LETTER A
+0xC1 0x0411 #CYRILLIC CAPITAL LETTER BE
+0xC2 0x0412 #CYRILLIC CAPITAL LETTER VE
+0xC3 0x0413 #CYRILLIC CAPITAL LETTER GHE
+0xC4 0x0414 #CYRILLIC CAPITAL LETTER DE
+0xC5 0x0415 #CYRILLIC CAPITAL LETTER IE
+0xC6 0x0416 #CYRILLIC CAPITAL LETTER ZHE
+0xC7 0x0417 #CYRILLIC CAPITAL LETTER ZE
+0xC8 0x0418 #CYRILLIC CAPITAL LETTER I
+0xC9 0x0419 #CYRILLIC CAPITAL LETTER SHORT I
+0xCA 0x041A #CYRILLIC CAPITAL LETTER KA
+0xCB 0x041B #CYRILLIC CAPITAL LETTER EL
+0xCC 0x041C #CYRILLIC CAPITAL LETTER EM
+0xCD 0x041D #CYRILLIC CAPITAL LETTER EN
+0xCE 0x041E #CYRILLIC CAPITAL LETTER O
+0xCF 0x041F #CYRILLIC CAPITAL LETTER PE
+0xD0 0x0420 #CYRILLIC CAPITAL LETTER ER
+0xD1 0x0421 #CYRILLIC CAPITAL LETTER ES
+0xD2 0x0422 #CYRILLIC CAPITAL LETTER TE
+0xD3 0x0423 #CYRILLIC CAPITAL LETTER U
+0xD4 0x0424 #CYRILLIC CAPITAL LETTER EF
+0xD5 0x0425 #CYRILLIC CAPITAL LETTER HA
+0xD6 0x0426 #CYRILLIC CAPITAL LETTER TSE
+0xD7 0x0427 #CYRILLIC CAPITAL LETTER CHE
+0xD8 0x0428 #CYRILLIC CAPITAL LETTER SHA
+0xD9 0x0429 #CYRILLIC CAPITAL LETTER SHCHA
+0xDA 0x042A #CYRILLIC CAPITAL LETTER HARD SIGN
+0xDB 0x042B #CYRILLIC CAPITAL LETTER YERU
+0xDC 0x042C #CYRILLIC CAPITAL LETTER SOFT SIGN
+0xDD 0x042D #CYRILLIC CAPITAL LETTER E
+0xDE 0x042E #CYRILLIC CAPITAL LETTER YU
+0xDF 0x042F #CYRILLIC CAPITAL LETTER YA
+0xE0 0x0430 #CYRILLIC SMALL LETTER A
+0xE1 0x0431 #CYRILLIC SMALL LETTER BE
+0xE2 0x0432 #CYRILLIC SMALL LETTER VE
+0xE3 0x0433 #CYRILLIC SMALL LETTER GHE
+0xE4 0x0434 #CYRILLIC SMALL LETTER DE
+0xE5 0x0435 #CYRILLIC SMALL LETTER IE
+0xE6 0x0436 #CYRILLIC SMALL LETTER ZHE
+0xE7 0x0437 #CYRILLIC SMALL LETTER ZE
+0xE8 0x0438 #CYRILLIC SMALL LETTER I
+0xE9 0x0439 #CYRILLIC SMALL LETTER SHORT I
+0xEA 0x043A #CYRILLIC SMALL LETTER KA
+0xEB 0x043B #CYRILLIC SMALL LETTER EL
+0xEC 0x043C #CYRILLIC SMALL LETTER EM
+0xED 0x043D #CYRILLIC SMALL LETTER EN
+0xEE 0x043E #CYRILLIC SMALL LETTER O
+0xEF 0x043F #CYRILLIC SMALL LETTER PE
+0xF0 0x0440 #CYRILLIC SMALL LETTER ER
+0xF1 0x0441 #CYRILLIC SMALL LETTER ES
+0xF2 0x0442 #CYRILLIC SMALL LETTER TE
+0xF3 0x0443 #CYRILLIC SMALL LETTER U
+0xF4 0x0444 #CYRILLIC SMALL LETTER EF
+0xF5 0x0445 #CYRILLIC SMALL LETTER HA
+0xF6 0x0446 #CYRILLIC SMALL LETTER TSE
+0xF7 0x0447 #CYRILLIC SMALL LETTER CHE
+0xF8 0x0448 #CYRILLIC SMALL LETTER SHA
+0xF9 0x0449 #CYRILLIC SMALL LETTER SHCHA
+0xFA 0x044A #CYRILLIC SMALL LETTER HARD SIGN
+0xFB 0x044B #CYRILLIC SMALL LETTER YERU
+0xFC 0x044C #CYRILLIC SMALL LETTER SOFT SIGN
+0xFD 0x044D #CYRILLIC SMALL LETTER E
+0xFE 0x044E #CYRILLIC SMALL LETTER YU
+0xFF 0x044F #CYRILLIC SMALL LETTER YA
--- /dev/null
+#
+# Name: cp1252 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1252 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1252 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89 0x2030 #PER MILLE SIGN
+0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C 0x0152 #LATIN CAPITAL LIGATURE OE
+0x8D #UNDEFINED
+0x8E 0x017D #LATIN CAPITAL LETTER Z WITH CARON
+0x8F #UNDEFINED
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 0x02DC #SMALL TILDE
+0x99 0x2122 #TRADE MARK SIGN
+0x9A 0x0161 #LATIN SMALL LETTER S WITH CARON
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C 0x0153 #LATIN SMALL LIGATURE OE
+0x9D #UNDEFINED
+0x9E 0x017E #LATIN SMALL LETTER Z WITH CARON
+0x9F 0x0178 #LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x00A1 #INVERTED EXCLAMATION MARK
+0xA2 0x00A2 #CENT SIGN
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x00A5 #YEN SIGN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x00AA #FEMININE ORDINAL INDICATOR
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x00AF #MACRON
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00B8 #CEDILLA
+0xB9 0x00B9 #SUPERSCRIPT ONE
+0xBA 0x00BA #MASCULINE ORDINAL INDICATOR
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC #VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS
+0xBF 0x00BF #INVERTED QUESTION MARK
+0xC0 0x00C0 #LATIN CAPITAL LETTER A WITH GRAVE
+0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x00C3 #LATIN CAPITAL LETTER A WITH TILDE
+0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x00C6 #LATIN CAPITAL LETTER AE
+0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x00C8 #LATIN CAPITAL LETTER E WITH GRAVE
+0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x00CA #LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x00CC #LATIN CAPITAL LETTER I WITH GRAVE
+0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x00CF #LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0 0x00D0 #LATIN CAPITAL LETTER ETH
+0xD1 0x00D1 #LATIN CAPITAL LETTER N WITH TILDE
+0xD2 0x00D2 #LATIN CAPITAL LETTER O WITH GRAVE
+0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x00D5 #LATIN CAPITAL LETTER O WITH TILDE
+0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 #MULTIPLICATION SIGN
+0xD8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE
+0xD9 0x00D9 #LATIN CAPITAL LETTER U WITH GRAVE
+0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB #LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x00DD #LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE 0x00DE #LATIN CAPITAL LETTER THORN
+0xDF 0x00DF #LATIN SMALL LETTER SHARP S
+0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x00E3 #LATIN SMALL LETTER A WITH TILDE
+0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x00E6 #LATIN SMALL LETTER AE
+0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x00EC #LATIN SMALL LETTER I WITH GRAVE
+0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS
+0xF0 0x00F0 #LATIN SMALL LETTER ETH
+0xF1 0x00F1 #LATIN SMALL LETTER N WITH TILDE
+0xF2 0x00F2 #LATIN SMALL LETTER O WITH GRAVE
+0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x00F5 #LATIN SMALL LETTER O WITH TILDE
+0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 #DIVISION SIGN
+0xF8 0x00F8 #LATIN SMALL LETTER O WITH STROKE
+0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x00FD #LATIN SMALL LETTER Y WITH ACUTE
+0xFE 0x00FE #LATIN SMALL LETTER THORN
+0xFF 0x00FF #LATIN SMALL LETTER Y WITH DIAERESIS
--- /dev/null
+#
+# Name: cp1253 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1253 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1253 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 #UNDEFINED
+0x89 0x2030 #PER MILLE SIGN
+0x8A #UNDEFINED
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C #UNDEFINED
+0x8D #UNDEFINED
+0x8E #UNDEFINED
+0x8F #UNDEFINED
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 #UNDEFINED
+0x99 0x2122 #TRADE MARK SIGN
+0x9A #UNDEFINED
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C #UNDEFINED
+0x9D #UNDEFINED
+0x9E #UNDEFINED
+0x9F #UNDEFINED
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x0385 #GREEK DIALYTIKA TONOS
+0xA2 0x0386 #GREEK CAPITAL LETTER ALPHA WITH TONOS
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x00A5 #YEN SIGN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA #UNDEFINED
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x2015 #HORIZONTAL BAR
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x0384 #GREEK TONOS
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x0388 #GREEK CAPITAL LETTER EPSILON WITH TONOS
+0xB9 0x0389 #GREEK CAPITAL LETTER ETA WITH TONOS
+0xBA 0x038A #GREEK CAPITAL LETTER IOTA WITH TONOS
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x038C #GREEK CAPITAL LETTER OMICRON WITH TONOS
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x038E #GREEK CAPITAL LETTER UPSILON WITH TONOS
+0xBF 0x038F #GREEK CAPITAL LETTER OMEGA WITH TONOS
+0xC0 0x0390 #GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0xC1 0x0391 #GREEK CAPITAL LETTER ALPHA
+0xC2 0x0392 #GREEK CAPITAL LETTER BETA
+0xC3 0x0393 #GREEK CAPITAL LETTER GAMMA
+0xC4 0x0394 #GREEK CAPITAL LETTER DELTA
+0xC5 0x0395 #GREEK CAPITAL LETTER EPSILON
+0xC6 0x0396 #GREEK CAPITAL LETTER ZETA
+0xC7 0x0397 #GREEK CAPITAL LETTER ETA
+0xC8 0x0398 #GREEK CAPITAL LETTER THETA
+0xC9 0x0399 #GREEK CAPITAL LETTER IOTA
+0xCA 0x039A #GREEK CAPITAL LETTER KAPPA
+0xCB 0x039B #GREEK CAPITAL LETTER LAMDA
+0xCC 0x039C #GREEK CAPITAL LETTER MU
+0xCD 0x039D #GREEK CAPITAL LETTER NU
+0xCE 0x039E #GREEK CAPITAL LETTER XI
+0xCF 0x039F #GREEK CAPITAL LETTER OMICRON
+0xD0 0x03A0 #GREEK CAPITAL LETTER PI
+0xD1 0x03A1 #GREEK CAPITAL LETTER RHO
+0xD2 #UNDEFINED
+0xD3 0x03A3 #GREEK CAPITAL LETTER SIGMA
+0xD4 0x03A4 #GREEK CAPITAL LETTER TAU
+0xD5 0x03A5 #GREEK CAPITAL LETTER UPSILON
+0xD6 0x03A6 #GREEK CAPITAL LETTER PHI
+0xD7 0x03A7 #GREEK CAPITAL LETTER CHI
+0xD8 0x03A8 #GREEK CAPITAL LETTER PSI
+0xD9 0x03A9 #GREEK CAPITAL LETTER OMEGA
+0xDA 0x03AA #GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0xDB 0x03AB #GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0xDC 0x03AC #GREEK SMALL LETTER ALPHA WITH TONOS
+0xDD 0x03AD #GREEK SMALL LETTER EPSILON WITH TONOS
+0xDE 0x03AE #GREEK SMALL LETTER ETA WITH TONOS
+0xDF 0x03AF #GREEK SMALL LETTER IOTA WITH TONOS
+0xE0 0x03B0 #GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0xE1 0x03B1 #GREEK SMALL LETTER ALPHA
+0xE2 0x03B2 #GREEK SMALL LETTER BETA
+0xE3 0x03B3 #GREEK SMALL LETTER GAMMA
+0xE4 0x03B4 #GREEK SMALL LETTER DELTA
+0xE5 0x03B5 #GREEK SMALL LETTER EPSILON
+0xE6 0x03B6 #GREEK SMALL LETTER ZETA
+0xE7 0x03B7 #GREEK SMALL LETTER ETA
+0xE8 0x03B8 #GREEK SMALL LETTER THETA
+0xE9 0x03B9 #GREEK SMALL LETTER IOTA
+0xEA 0x03BA #GREEK SMALL LETTER KAPPA
+0xEB 0x03BB #GREEK SMALL LETTER LAMDA
+0xEC 0x03BC #GREEK SMALL LETTER MU
+0xED 0x03BD #GREEK SMALL LETTER NU
+0xEE 0x03BE #GREEK SMALL LETTER XI
+0xEF 0x03BF #GREEK SMALL LETTER OMICRON
+0xF0 0x03C0 #GREEK SMALL LETTER PI
+0xF1 0x03C1 #GREEK SMALL LETTER RHO
+0xF2 0x03C2 #GREEK SMALL LETTER FINAL SIGMA
+0xF3 0x03C3 #GREEK SMALL LETTER SIGMA
+0xF4 0x03C4 #GREEK SMALL LETTER TAU
+0xF5 0x03C5 #GREEK SMALL LETTER UPSILON
+0xF6 0x03C6 #GREEK SMALL LETTER PHI
+0xF7 0x03C7 #GREEK SMALL LETTER CHI
+0xF8 0x03C8 #GREEK SMALL LETTER PSI
+0xF9 0x03C9 #GREEK SMALL LETTER OMEGA
+0xFA 0x03CA #GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0xFB 0x03CB #GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0xFC 0x03CC #GREEK SMALL LETTER OMICRON WITH TONOS
+0xFD 0x03CD #GREEK SMALL LETTER UPSILON WITH TONOS
+0xFE 0x03CE #GREEK SMALL LETTER OMEGA WITH TONOS
+0xFF #UNDEFINED
--- /dev/null
+#
+# Name: cp1254 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1254 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1254 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89 0x2030 #PER MILLE SIGN
+0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C 0x0152 #LATIN CAPITAL LIGATURE OE
+0x8D #UNDEFINED
+0x8E #UNDEFINED
+0x8F #UNDEFINED
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 0x02DC #SMALL TILDE
+0x99 0x2122 #TRADE MARK SIGN
+0x9A 0x0161 #LATIN SMALL LETTER S WITH CARON
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C 0x0153 #LATIN SMALL LIGATURE OE
+0x9D #UNDEFINED
+0x9E #UNDEFINED
+0x9F 0x0178 #LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x00A1 #INVERTED EXCLAMATION MARK
+0xA2 0x00A2 #CENT SIGN
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x00A5 #YEN SIGN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x00AA #FEMININE ORDINAL INDICATOR
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x00AF #MACRON
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00B8 #CEDILLA
+0xB9 0x00B9 #SUPERSCRIPT ONE
+0xBA 0x00BA #MASCULINE ORDINAL INDICATOR
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC #VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS
+0xBF 0x00BF #INVERTED QUESTION MARK
+0xC0 0x00C0 #LATIN CAPITAL LETTER A WITH GRAVE
+0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x00C3 #LATIN CAPITAL LETTER A WITH TILDE
+0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x00C6 #LATIN CAPITAL LETTER AE
+0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x00C8 #LATIN CAPITAL LETTER E WITH GRAVE
+0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x00CA #LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x00CC #LATIN CAPITAL LETTER I WITH GRAVE
+0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x00CF #LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0 0x011E #LATIN CAPITAL LETTER G WITH BREVE
+0xD1 0x00D1 #LATIN CAPITAL LETTER N WITH TILDE
+0xD2 0x00D2 #LATIN CAPITAL LETTER O WITH GRAVE
+0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x00D5 #LATIN CAPITAL LETTER O WITH TILDE
+0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 #MULTIPLICATION SIGN
+0xD8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE
+0xD9 0x00D9 #LATIN CAPITAL LETTER U WITH GRAVE
+0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB #LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x0130 #LATIN CAPITAL LETTER I WITH DOT ABOVE
+0xDE 0x015E #LATIN CAPITAL LETTER S WITH CEDILLA
+0xDF 0x00DF #LATIN SMALL LETTER SHARP S
+0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x00E3 #LATIN SMALL LETTER A WITH TILDE
+0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x00E6 #LATIN SMALL LETTER AE
+0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x00EC #LATIN SMALL LETTER I WITH GRAVE
+0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS
+0xF0 0x011F #LATIN SMALL LETTER G WITH BREVE
+0xF1 0x00F1 #LATIN SMALL LETTER N WITH TILDE
+0xF2 0x00F2 #LATIN SMALL LETTER O WITH GRAVE
+0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x00F5 #LATIN SMALL LETTER O WITH TILDE
+0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 #DIVISION SIGN
+0xF8 0x00F8 #LATIN SMALL LETTER O WITH STROKE
+0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x0131 #LATIN SMALL LETTER DOTLESS I
+0xFE 0x015F #LATIN SMALL LETTER S WITH CEDILLA
+0xFF 0x00FF #LATIN SMALL LETTER Y WITH DIAERESIS
--- /dev/null
+#
+# Name: cp1255 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 1/7/2000
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1255 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1255 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89 0x2030 #PER MILLE SIGN
+0x8A #UNDEFINED
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C #UNDEFINED
+0x8D #UNDEFINED
+0x8E #UNDEFINED
+0x8F #UNDEFINED
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 0x02DC #SMALL TILDE
+0x99 0x2122 #TRADE MARK SIGN
+0x9A #UNDEFINED
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C #UNDEFINED
+0x9D #UNDEFINED
+0x9E #UNDEFINED
+0x9F #UNDEFINED
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x00A1 #INVERTED EXCLAMATION MARK
+0xA2 0x00A2 #CENT SIGN
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x20AA #NEW SHEQEL SIGN
+0xA5 0x00A5 #YEN SIGN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x00D7 #MULTIPLICATION SIGN
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x00AF #MACRON
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00B8 #CEDILLA
+0xB9 0x00B9 #SUPERSCRIPT ONE
+0xBA 0x00F7 #DIVISION SIGN
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC #VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS
+0xBF 0x00BF #INVERTED QUESTION MARK
+0xC0 0x05B0 #HEBREW POINT SHEVA
+0xC1 0x05B1 #HEBREW POINT HATAF SEGOL
+0xC2 0x05B2 #HEBREW POINT HATAF PATAH
+0xC3 0x05B3 #HEBREW POINT HATAF QAMATS
+0xC4 0x05B4 #HEBREW POINT HIRIQ
+0xC5 0x05B5 #HEBREW POINT TSERE
+0xC6 0x05B6 #HEBREW POINT SEGOL
+0xC7 0x05B7 #HEBREW POINT PATAH
+0xC8 0x05B8 #HEBREW POINT QAMATS
+0xC9 0x05B9 #HEBREW POINT HOLAM
+0xCA #UNDEFINED
+0xCB 0x05BB #HEBREW POINT QUBUTS
+0xCC 0x05BC #HEBREW POINT DAGESH OR MAPIQ
+0xCD 0x05BD #HEBREW POINT METEG
+0xCE 0x05BE #HEBREW PUNCTUATION MAQAF
+0xCF 0x05BF #HEBREW POINT RAFE
+0xD0 0x05C0 #HEBREW PUNCTUATION PASEQ
+0xD1 0x05C1 #HEBREW POINT SHIN DOT
+0xD2 0x05C2 #HEBREW POINT SIN DOT
+0xD3 0x05C3 #HEBREW PUNCTUATION SOF PASUQ
+0xD4 0x05F0 #HEBREW LIGATURE YIDDISH DOUBLE VAV
+0xD5 0x05F1 #HEBREW LIGATURE YIDDISH VAV YOD
+0xD6 0x05F2 #HEBREW LIGATURE YIDDISH DOUBLE YOD
+0xD7 0x05F3 #HEBREW PUNCTUATION GERESH
+0xD8 0x05F4 #HEBREW PUNCTUATION GERSHAYIM
+0xD9 #UNDEFINED
+0xDA #UNDEFINED
+0xDB #UNDEFINED
+0xDC #UNDEFINED
+0xDD #UNDEFINED
+0xDE #UNDEFINED
+0xDF #UNDEFINED
+0xE0 0x05D0 #HEBREW LETTER ALEF
+0xE1 0x05D1 #HEBREW LETTER BET
+0xE2 0x05D2 #HEBREW LETTER GIMEL
+0xE3 0x05D3 #HEBREW LETTER DALET
+0xE4 0x05D4 #HEBREW LETTER HE
+0xE5 0x05D5 #HEBREW LETTER VAV
+0xE6 0x05D6 #HEBREW LETTER ZAYIN
+0xE7 0x05D7 #HEBREW LETTER HET
+0xE8 0x05D8 #HEBREW LETTER TET
+0xE9 0x05D9 #HEBREW LETTER YOD
+0xEA 0x05DA #HEBREW LETTER FINAL KAF
+0xEB 0x05DB #HEBREW LETTER KAF
+0xEC 0x05DC #HEBREW LETTER LAMED
+0xED 0x05DD #HEBREW LETTER FINAL MEM
+0xEE 0x05DE #HEBREW LETTER MEM
+0xEF 0x05DF #HEBREW LETTER FINAL NUN
+0xF0 0x05E0 #HEBREW LETTER NUN
+0xF1 0x05E1 #HEBREW LETTER SAMEKH
+0xF2 0x05E2 #HEBREW LETTER AYIN
+0xF3 0x05E3 #HEBREW LETTER FINAL PE
+0xF4 0x05E4 #HEBREW LETTER PE
+0xF5 0x05E5 #HEBREW LETTER FINAL TSADI
+0xF6 0x05E6 #HEBREW LETTER TSADI
+0xF7 0x05E7 #HEBREW LETTER QOF
+0xF8 0x05E8 #HEBREW LETTER RESH
+0xF9 0x05E9 #HEBREW LETTER SHIN
+0xFA 0x05EA #HEBREW LETTER TAV
+0xFB #UNDEFINED
+0xFC #UNDEFINED
+0xFD 0x200E #LEFT-TO-RIGHT MARK
+0xFE 0x200F #RIGHT-TO-LEFT MARK
+0xFF #UNDEFINED
--- /dev/null
+#
+# Name: cp1256 to Unicode table
+# Unicode version: 2.1
+# Table version: 2.01
+# Table format: Format A
+# Date: 01/5/99
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1256 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1256 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 0x067E #ARABIC LETTER PEH
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89 0x2030 #PER MILLE SIGN
+0x8A 0x0679 #ARABIC LETTER TTEH
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C 0x0152 #LATIN CAPITAL LIGATURE OE
+0x8D 0x0686 #ARABIC LETTER TCHEH
+0x8E 0x0698 #ARABIC LETTER JEH
+0x8F 0x0688 #ARABIC LETTER DDAL
+0x90 0x06AF #ARABIC LETTER GAF
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 0x06A9 #ARABIC LETTER KEHEH
+0x99 0x2122 #TRADE MARK SIGN
+0x9A 0x0691 #ARABIC LETTER RREH
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C 0x0153 #LATIN SMALL LIGATURE OE
+0x9D 0x200C #ZERO WIDTH NON-JOINER
+0x9E 0x200D #ZERO WIDTH JOINER
+0x9F 0x06BA #ARABIC LETTER NOON GHUNNA
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x060C #ARABIC COMMA
+0xA2 0x00A2 #CENT SIGN
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x00A5 #YEN SIGN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x06BE #ARABIC LETTER HEH DOACHASHMEE
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x00AF #MACRON
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00B8 #CEDILLA
+0xB9 0x00B9 #SUPERSCRIPT ONE
+0xBA 0x061B #ARABIC SEMICOLON
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC #VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS
+0xBF 0x061F #ARABIC QUESTION MARK
+0xC0 0x06C1 #ARABIC LETTER HEH GOAL
+0xC1 0x0621 #ARABIC LETTER HAMZA
+0xC2 0x0622 #ARABIC LETTER ALEF WITH MADDA ABOVE
+0xC3 0x0623 #ARABIC LETTER ALEF WITH HAMZA ABOVE
+0xC4 0x0624 #ARABIC LETTER WAW WITH HAMZA ABOVE
+0xC5 0x0625 #ARABIC LETTER ALEF WITH HAMZA BELOW
+0xC6 0x0626 #ARABIC LETTER YEH WITH HAMZA ABOVE
+0xC7 0x0627 #ARABIC LETTER ALEF
+0xC8 0x0628 #ARABIC LETTER BEH
+0xC9 0x0629 #ARABIC LETTER TEH MARBUTA
+0xCA 0x062A #ARABIC LETTER TEH
+0xCB 0x062B #ARABIC LETTER THEH
+0xCC 0x062C #ARABIC LETTER JEEM
+0xCD 0x062D #ARABIC LETTER HAH
+0xCE 0x062E #ARABIC LETTER KHAH
+0xCF 0x062F #ARABIC LETTER DAL
+0xD0 0x0630 #ARABIC LETTER THAL
+0xD1 0x0631 #ARABIC LETTER REH
+0xD2 0x0632 #ARABIC LETTER ZAIN
+0xD3 0x0633 #ARABIC LETTER SEEN
+0xD4 0x0634 #ARABIC LETTER SHEEN
+0xD5 0x0635 #ARABIC LETTER SAD
+0xD6 0x0636 #ARABIC LETTER DAD
+0xD7 0x00D7 #MULTIPLICATION SIGN
+0xD8 0x0637 #ARABIC LETTER TAH
+0xD9 0x0638 #ARABIC LETTER ZAH
+0xDA 0x0639 #ARABIC LETTER AIN
+0xDB 0x063A #ARABIC LETTER GHAIN
+0xDC 0x0640 #ARABIC TATWEEL
+0xDD 0x0641 #ARABIC LETTER FEH
+0xDE 0x0642 #ARABIC LETTER QAF
+0xDF 0x0643 #ARABIC LETTER KAF
+0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x0644 #ARABIC LETTER LAM
+0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x0645 #ARABIC LETTER MEEM
+0xE4 0x0646 #ARABIC LETTER NOON
+0xE5 0x0647 #ARABIC LETTER HEH
+0xE6 0x0648 #ARABIC LETTER WAW
+0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x0649 #ARABIC LETTER ALEF MAKSURA
+0xED 0x064A #ARABIC LETTER YEH
+0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS
+0xF0 0x064B #ARABIC FATHATAN
+0xF1 0x064C #ARABIC DAMMATAN
+0xF2 0x064D #ARABIC KASRATAN
+0xF3 0x064E #ARABIC FATHA
+0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x064F #ARABIC DAMMA
+0xF6 0x0650 #ARABIC KASRA
+0xF7 0x00F7 #DIVISION SIGN
+0xF8 0x0651 #ARABIC SHADDA
+0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x0652 #ARABIC SUKUN
+0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x200E #LEFT-TO-RIGHT MARK
+0xFE 0x200F #RIGHT-TO-LEFT MARK
+0xFF 0x06D2 #ARABIC LETTER YEH BARREE
--- /dev/null
+#
+# Name: cp1257 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1257 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1257 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 #UNDEFINED
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 #UNDEFINED
+0x89 0x2030 #PER MILLE SIGN
+0x8A #UNDEFINED
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C #UNDEFINED
+0x8D 0x00A8 #DIAERESIS
+0x8E 0x02C7 #CARON
+0x8F 0x00B8 #CEDILLA
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 #UNDEFINED
+0x99 0x2122 #TRADE MARK SIGN
+0x9A #UNDEFINED
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C #UNDEFINED
+0x9D 0x00AF #MACRON
+0x9E 0x02DB #OGONEK
+0x9F #UNDEFINED
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 #UNDEFINED
+0xA2 0x00A2 #CENT SIGN
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 #UNDEFINED
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x0156 #LATIN CAPITAL LETTER R WITH CEDILLA
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x00C6 #LATIN CAPITAL LETTER AE
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00F8 #LATIN SMALL LETTER O WITH STROKE
+0xB9 0x00B9 #SUPERSCRIPT ONE
+0xBA 0x0157 #LATIN SMALL LETTER R WITH CEDILLA
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC #VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS
+0xBF 0x00E6 #LATIN SMALL LETTER AE
+0xC0 0x0104 #LATIN CAPITAL LETTER A WITH OGONEK
+0xC1 0x012E #LATIN CAPITAL LETTER I WITH OGONEK
+0xC2 0x0100 #LATIN CAPITAL LETTER A WITH MACRON
+0xC3 0x0106 #LATIN CAPITAL LETTER C WITH ACUTE
+0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x0118 #LATIN CAPITAL LETTER E WITH OGONEK
+0xC7 0x0112 #LATIN CAPITAL LETTER E WITH MACRON
+0xC8 0x010C #LATIN CAPITAL LETTER C WITH CARON
+0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x0179 #LATIN CAPITAL LETTER Z WITH ACUTE
+0xCB 0x0116 #LATIN CAPITAL LETTER E WITH DOT ABOVE
+0xCC 0x0122 #LATIN CAPITAL LETTER G WITH CEDILLA
+0xCD 0x0136 #LATIN CAPITAL LETTER K WITH CEDILLA
+0xCE 0x012A #LATIN CAPITAL LETTER I WITH MACRON
+0xCF 0x013B #LATIN CAPITAL LETTER L WITH CEDILLA
+0xD0 0x0160 #LATIN CAPITAL LETTER S WITH CARON
+0xD1 0x0143 #LATIN CAPITAL LETTER N WITH ACUTE
+0xD2 0x0145 #LATIN CAPITAL LETTER N WITH CEDILLA
+0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x014C #LATIN CAPITAL LETTER O WITH MACRON
+0xD5 0x00D5 #LATIN CAPITAL LETTER O WITH TILDE
+0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 #MULTIPLICATION SIGN
+0xD8 0x0172 #LATIN CAPITAL LETTER U WITH OGONEK
+0xD9 0x0141 #LATIN CAPITAL LETTER L WITH STROKE
+0xDA 0x015A #LATIN CAPITAL LETTER S WITH ACUTE
+0xDB 0x016A #LATIN CAPITAL LETTER U WITH MACRON
+0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x017B #LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xDE 0x017D #LATIN CAPITAL LETTER Z WITH CARON
+0xDF 0x00DF #LATIN SMALL LETTER SHARP S
+0xE0 0x0105 #LATIN SMALL LETTER A WITH OGONEK
+0xE1 0x012F #LATIN SMALL LETTER I WITH OGONEK
+0xE2 0x0101 #LATIN SMALL LETTER A WITH MACRON
+0xE3 0x0107 #LATIN SMALL LETTER C WITH ACUTE
+0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x0119 #LATIN SMALL LETTER E WITH OGONEK
+0xE7 0x0113 #LATIN SMALL LETTER E WITH MACRON
+0xE8 0x010D #LATIN SMALL LETTER C WITH CARON
+0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x017A #LATIN SMALL LETTER Z WITH ACUTE
+0xEB 0x0117 #LATIN SMALL LETTER E WITH DOT ABOVE
+0xEC 0x0123 #LATIN SMALL LETTER G WITH CEDILLA
+0xED 0x0137 #LATIN SMALL LETTER K WITH CEDILLA
+0xEE 0x012B #LATIN SMALL LETTER I WITH MACRON
+0xEF 0x013C #LATIN SMALL LETTER L WITH CEDILLA
+0xF0 0x0161 #LATIN SMALL LETTER S WITH CARON
+0xF1 0x0144 #LATIN SMALL LETTER N WITH ACUTE
+0xF2 0x0146 #LATIN SMALL LETTER N WITH CEDILLA
+0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x014D #LATIN SMALL LETTER O WITH MACRON
+0xF5 0x00F5 #LATIN SMALL LETTER O WITH TILDE
+0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 #DIVISION SIGN
+0xF8 0x0173 #LATIN SMALL LETTER U WITH OGONEK
+0xF9 0x0142 #LATIN SMALL LETTER L WITH STROKE
+0xFA 0x015B #LATIN SMALL LETTER S WITH ACUTE
+0xFB 0x016B #LATIN SMALL LETTER U WITH MACRON
+0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x017C #LATIN SMALL LETTER Z WITH DOT ABOVE
+0xFE 0x017E #LATIN SMALL LETTER Z WITH CARON
+0xFF 0x02D9 #DOT ABOVE
--- /dev/null
+#
+# Name: cp1258 to Unicode table
+# Unicode version: 2.0
+# Table version: 2.01
+# Table format: Format A
+# Date: 04/15/98
+#
+# Contact: cpxlate@microsoft.com
+#
+# General notes: none
+#
+# Format: Three tab-separated columns
+# Column #1 is the cp1258 code (in hex)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 is the Unicode name (follows a comment sign, '#')
+#
+# The entries are in cp1258 order
+#
+0x00 0x0000 #NULL
+0x01 0x0001 #START OF HEADING
+0x02 0x0002 #START OF TEXT
+0x03 0x0003 #END OF TEXT
+0x04 0x0004 #END OF TRANSMISSION
+0x05 0x0005 #ENQUIRY
+0x06 0x0006 #ACKNOWLEDGE
+0x07 0x0007 #BELL
+0x08 0x0008 #BACKSPACE
+0x09 0x0009 #HORIZONTAL TABULATION
+0x0A 0x000A #LINE FEED
+0x0B 0x000B #VERTICAL TABULATION
+0x0C 0x000C #FORM FEED
+0x0D 0x000D #CARRIAGE RETURN
+0x0E 0x000E #SHIFT OUT
+0x0F 0x000F #SHIFT IN
+0x10 0x0010 #DATA LINK ESCAPE
+0x11 0x0011 #DEVICE CONTROL ONE
+0x12 0x0012 #DEVICE CONTROL TWO
+0x13 0x0013 #DEVICE CONTROL THREE
+0x14 0x0014 #DEVICE CONTROL FOUR
+0x15 0x0015 #NEGATIVE ACKNOWLEDGE
+0x16 0x0016 #SYNCHRONOUS IDLE
+0x17 0x0017 #END OF TRANSMISSION BLOCK
+0x18 0x0018 #CANCEL
+0x19 0x0019 #END OF MEDIUM
+0x1A 0x001A #SUBSTITUTE
+0x1B 0x001B #ESCAPE
+0x1C 0x001C #FILE SEPARATOR
+0x1D 0x001D #GROUP SEPARATOR
+0x1E 0x001E #RECORD SEPARATOR
+0x1F 0x001F #UNIT SEPARATOR
+0x20 0x0020 #SPACE
+0x21 0x0021 #EXCLAMATION MARK
+0x22 0x0022 #QUOTATION MARK
+0x23 0x0023 #NUMBER SIGN
+0x24 0x0024 #DOLLAR SIGN
+0x25 0x0025 #PERCENT SIGN
+0x26 0x0026 #AMPERSAND
+0x27 0x0027 #APOSTROPHE
+0x28 0x0028 #LEFT PARENTHESIS
+0x29 0x0029 #RIGHT PARENTHESIS
+0x2A 0x002A #ASTERISK
+0x2B 0x002B #PLUS SIGN
+0x2C 0x002C #COMMA
+0x2D 0x002D #HYPHEN-MINUS
+0x2E 0x002E #FULL STOP
+0x2F 0x002F #SOLIDUS
+0x30 0x0030 #DIGIT ZERO
+0x31 0x0031 #DIGIT ONE
+0x32 0x0032 #DIGIT TWO
+0x33 0x0033 #DIGIT THREE
+0x34 0x0034 #DIGIT FOUR
+0x35 0x0035 #DIGIT FIVE
+0x36 0x0036 #DIGIT SIX
+0x37 0x0037 #DIGIT SEVEN
+0x38 0x0038 #DIGIT EIGHT
+0x39 0x0039 #DIGIT NINE
+0x3A 0x003A #COLON
+0x3B 0x003B #SEMICOLON
+0x3C 0x003C #LESS-THAN SIGN
+0x3D 0x003D #EQUALS SIGN
+0x3E 0x003E #GREATER-THAN SIGN
+0x3F 0x003F #QUESTION MARK
+0x40 0x0040 #COMMERCIAL AT
+0x41 0x0041 #LATIN CAPITAL LETTER A
+0x42 0x0042 #LATIN CAPITAL LETTER B
+0x43 0x0043 #LATIN CAPITAL LETTER C
+0x44 0x0044 #LATIN CAPITAL LETTER D
+0x45 0x0045 #LATIN CAPITAL LETTER E
+0x46 0x0046 #LATIN CAPITAL LETTER F
+0x47 0x0047 #LATIN CAPITAL LETTER G
+0x48 0x0048 #LATIN CAPITAL LETTER H
+0x49 0x0049 #LATIN CAPITAL LETTER I
+0x4A 0x004A #LATIN CAPITAL LETTER J
+0x4B 0x004B #LATIN CAPITAL LETTER K
+0x4C 0x004C #LATIN CAPITAL LETTER L
+0x4D 0x004D #LATIN CAPITAL LETTER M
+0x4E 0x004E #LATIN CAPITAL LETTER N
+0x4F 0x004F #LATIN CAPITAL LETTER O
+0x50 0x0050 #LATIN CAPITAL LETTER P
+0x51 0x0051 #LATIN CAPITAL LETTER Q
+0x52 0x0052 #LATIN CAPITAL LETTER R
+0x53 0x0053 #LATIN CAPITAL LETTER S
+0x54 0x0054 #LATIN CAPITAL LETTER T
+0x55 0x0055 #LATIN CAPITAL LETTER U
+0x56 0x0056 #LATIN CAPITAL LETTER V
+0x57 0x0057 #LATIN CAPITAL LETTER W
+0x58 0x0058 #LATIN CAPITAL LETTER X
+0x59 0x0059 #LATIN CAPITAL LETTER Y
+0x5A 0x005A #LATIN CAPITAL LETTER Z
+0x5B 0x005B #LEFT SQUARE BRACKET
+0x5C 0x005C #REVERSE SOLIDUS
+0x5D 0x005D #RIGHT SQUARE BRACKET
+0x5E 0x005E #CIRCUMFLEX ACCENT
+0x5F 0x005F #LOW LINE
+0x60 0x0060 #GRAVE ACCENT
+0x61 0x0061 #LATIN SMALL LETTER A
+0x62 0x0062 #LATIN SMALL LETTER B
+0x63 0x0063 #LATIN SMALL LETTER C
+0x64 0x0064 #LATIN SMALL LETTER D
+0x65 0x0065 #LATIN SMALL LETTER E
+0x66 0x0066 #LATIN SMALL LETTER F
+0x67 0x0067 #LATIN SMALL LETTER G
+0x68 0x0068 #LATIN SMALL LETTER H
+0x69 0x0069 #LATIN SMALL LETTER I
+0x6A 0x006A #LATIN SMALL LETTER J
+0x6B 0x006B #LATIN SMALL LETTER K
+0x6C 0x006C #LATIN SMALL LETTER L
+0x6D 0x006D #LATIN SMALL LETTER M
+0x6E 0x006E #LATIN SMALL LETTER N
+0x6F 0x006F #LATIN SMALL LETTER O
+0x70 0x0070 #LATIN SMALL LETTER P
+0x71 0x0071 #LATIN SMALL LETTER Q
+0x72 0x0072 #LATIN SMALL LETTER R
+0x73 0x0073 #LATIN SMALL LETTER S
+0x74 0x0074 #LATIN SMALL LETTER T
+0x75 0x0075 #LATIN SMALL LETTER U
+0x76 0x0076 #LATIN SMALL LETTER V
+0x77 0x0077 #LATIN SMALL LETTER W
+0x78 0x0078 #LATIN SMALL LETTER X
+0x79 0x0079 #LATIN SMALL LETTER Y
+0x7A 0x007A #LATIN SMALL LETTER Z
+0x7B 0x007B #LEFT CURLY BRACKET
+0x7C 0x007C #VERTICAL LINE
+0x7D 0x007D #RIGHT CURLY BRACKET
+0x7E 0x007E #TILDE
+0x7F 0x007F #DELETE
+0x80 0x20AC #EURO SIGN
+0x81 #UNDEFINED
+0x82 0x201A #SINGLE LOW-9 QUOTATION MARK
+0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK
+0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK
+0x85 0x2026 #HORIZONTAL ELLIPSIS
+0x86 0x2020 #DAGGER
+0x87 0x2021 #DOUBLE DAGGER
+0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89 0x2030 #PER MILLE SIGN
+0x8A #UNDEFINED
+0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C 0x0152 #LATIN CAPITAL LIGATURE OE
+0x8D #UNDEFINED
+0x8E #UNDEFINED
+0x8F #UNDEFINED
+0x90 #UNDEFINED
+0x91 0x2018 #LEFT SINGLE QUOTATION MARK
+0x92 0x2019 #RIGHT SINGLE QUOTATION MARK
+0x93 0x201C #LEFT DOUBLE QUOTATION MARK
+0x94 0x201D #RIGHT DOUBLE QUOTATION MARK
+0x95 0x2022 #BULLET
+0x96 0x2013 #EN DASH
+0x97 0x2014 #EM DASH
+0x98 0x02DC #SMALL TILDE
+0x99 0x2122 #TRADE MARK SIGN
+0x9A #UNDEFINED
+0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C 0x0153 #LATIN SMALL LIGATURE OE
+0x9D #UNDEFINED
+0x9E #UNDEFINED
+0x9F 0x0178 #LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xA0 0x00A0 #NO-BREAK SPACE
+0xA1 0x00A1 #INVERTED EXCLAMATION MARK
+0xA2 0x00A2 #CENT SIGN
+0xA3 0x00A3 #POUND SIGN
+0xA4 0x00A4 #CURRENCY SIGN
+0xA5 0x00A5 #YEN SIGN
+0xA6 0x00A6 #BROKEN BAR
+0xA7 0x00A7 #SECTION SIGN
+0xA8 0x00A8 #DIAERESIS
+0xA9 0x00A9 #COPYRIGHT SIGN
+0xAA 0x00AA #FEMININE ORDINAL INDICATOR
+0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC #NOT SIGN
+0xAD 0x00AD #SOFT HYPHEN
+0xAE 0x00AE #REGISTERED SIGN
+0xAF 0x00AF #MACRON
+0xB0 0x00B0 #DEGREE SIGN
+0xB1 0x00B1 #PLUS-MINUS SIGN
+0xB2 0x00B2 #SUPERSCRIPT TWO
+0xB3 0x00B3 #SUPERSCRIPT THREE
+0xB4 0x00B4 #ACUTE ACCENT
+0xB5 0x00B5 #MICRO SIGN
+0xB6 0x00B6 #PILCROW SIGN
+0xB7 0x00B7 #MIDDLE DOT
+0xB8 0x00B8 #CEDILLA
+0xB9 0x00B9 #SUPERSCRIPT ONE
+0xBA 0x00BA #MASCULINE ORDINAL INDICATOR
+0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC #VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD #VULGAR FRACTION ONE HALF
+0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS
+0xBF 0x00BF #INVERTED QUESTION MARK
+0xC0 0x00C0 #LATIN CAPITAL LETTER A WITH GRAVE
+0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x0102 #LATIN CAPITAL LETTER A WITH BREVE
+0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x00C6 #LATIN CAPITAL LETTER AE
+0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x00C8 #LATIN CAPITAL LETTER E WITH GRAVE
+0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x00CA #LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x0300 #COMBINING GRAVE ACCENT
+0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x00CF #LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0 0x0110 #LATIN CAPITAL LETTER D WITH STROKE
+0xD1 0x00D1 #LATIN CAPITAL LETTER N WITH TILDE
+0xD2 0x0309 #COMBINING HOOK ABOVE
+0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x01A0 #LATIN CAPITAL LETTER O WITH HORN
+0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 #MULTIPLICATION SIGN
+0xD8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE
+0xD9 0x00D9 #LATIN CAPITAL LETTER U WITH GRAVE
+0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB #LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x01AF #LATIN CAPITAL LETTER U WITH HORN
+0xDE 0x0303 #COMBINING TILDE
+0xDF 0x00DF #LATIN SMALL LETTER SHARP S
+0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x0103 #LATIN SMALL LETTER A WITH BREVE
+0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x00E6 #LATIN SMALL LETTER AE
+0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x0301 #COMBINING ACUTE ACCENT
+0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS
+0xF0 0x0111 #LATIN SMALL LETTER D WITH STROKE
+0xF1 0x00F1 #LATIN SMALL LETTER N WITH TILDE
+0xF2 0x0323 #COMBINING DOT BELOW
+0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x01A1 #LATIN SMALL LETTER O WITH HORN
+0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 #DIVISION SIGN
+0xF8 0x00F8 #LATIN SMALL LETTER O WITH STROKE
+0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x01B0 #LATIN SMALL LETTER U WITH HORN
+0xFE 0x20AB #DONG SIGN
+0xFF 0x00FF #LATIN SMALL LETTER Y WITH DIAERESIS
--- /dev/null
+#
+# Name: ISO/IEC 8859-1:1998 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-1:1998 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-1 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x00A1 # INVERTED EXCLAMATION MARK
+0xA2 0x00A2 # CENT SIGN
+0xA3 0x00A3 # POUND SIGN
+0xA4 0x00A4 # CURRENCY SIGN
+0xA5 0x00A5 # YEN SIGN
+0xA6 0x00A6 # BROKEN BAR
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x00A9 # COPYRIGHT SIGN
+0xAA 0x00AA # FEMININE ORDINAL INDICATOR
+0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC # NOT SIGN
+0xAD 0x00AD # SOFT HYPHEN
+0xAE 0x00AE # REGISTERED SIGN
+0xAF 0x00AF # MACRON
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x00B1 # PLUS-MINUS SIGN
+0xB2 0x00B2 # SUPERSCRIPT TWO
+0xB3 0x00B3 # SUPERSCRIPT THREE
+0xB4 0x00B4 # ACUTE ACCENT
+0xB5 0x00B5 # MICRO SIGN
+0xB6 0x00B6 # PILCROW SIGN
+0xB7 0x00B7 # MIDDLE DOT
+0xB8 0x00B8 # CEDILLA
+0xB9 0x00B9 # SUPERSCRIPT ONE
+0xBA 0x00BA # MASCULINE ORDINAL INDICATOR
+0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC # VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD # VULGAR FRACTION ONE HALF
+0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS
+0xBF 0x00BF # INVERTED QUESTION MARK
+0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE
+0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE
+0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x00C6 # LATIN CAPITAL LETTER AE
+0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE
+0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE
+0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0 0x00D0 # LATIN CAPITAL LETTER ETH (Icelandic)
+0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE
+0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE
+0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE
+0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 # MULTIPLICATION SIGN
+0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE
+0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE
+0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE 0x00DE # LATIN CAPITAL LETTER THORN (Icelandic)
+0xDF 0x00DF # LATIN SMALL LETTER SHARP S (German)
+0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE
+0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x00E6 # LATIN SMALL LETTER AE
+0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE
+0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS
+0xF0 0x00F0 # LATIN SMALL LETTER ETH (Icelandic)
+0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE
+0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE
+0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE
+0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 # DIVISION SIGN
+0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE
+0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE
+0xFE 0x00FE # LATIN SMALL LETTER THORN (Icelandic)
+0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS
--- /dev/null
+#
+# Name: ISO 8859-2:1999 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-2:1999 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-2 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-2 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK
+0xA2 0x02D8 # BREVE
+0xA3 0x0141 # LATIN CAPITAL LETTER L WITH STROKE
+0xA4 0x00A4 # CURRENCY SIGN
+0xA5 0x013D # LATIN CAPITAL LETTER L WITH CARON
+0xA6 0x015A # LATIN CAPITAL LETTER S WITH ACUTE
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x0160 # LATIN CAPITAL LETTER S WITH CARON
+0xAA 0x015E # LATIN CAPITAL LETTER S WITH CEDILLA
+0xAB 0x0164 # LATIN CAPITAL LETTER T WITH CARON
+0xAC 0x0179 # LATIN CAPITAL LETTER Z WITH ACUTE
+0xAD 0x00AD # SOFT HYPHEN
+0xAE 0x017D # LATIN CAPITAL LETTER Z WITH CARON
+0xAF 0x017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x0105 # LATIN SMALL LETTER A WITH OGONEK
+0xB2 0x02DB # OGONEK
+0xB3 0x0142 # LATIN SMALL LETTER L WITH STROKE
+0xB4 0x00B4 # ACUTE ACCENT
+0xB5 0x013E # LATIN SMALL LETTER L WITH CARON
+0xB6 0x015B # LATIN SMALL LETTER S WITH ACUTE
+0xB7 0x02C7 # CARON
+0xB8 0x00B8 # CEDILLA
+0xB9 0x0161 # LATIN SMALL LETTER S WITH CARON
+0xBA 0x015F # LATIN SMALL LETTER S WITH CEDILLA
+0xBB 0x0165 # LATIN SMALL LETTER T WITH CARON
+0xBC 0x017A # LATIN SMALL LETTER Z WITH ACUTE
+0xBD 0x02DD # DOUBLE ACUTE ACCENT
+0xBE 0x017E # LATIN SMALL LETTER Z WITH CARON
+0xBF 0x017C # LATIN SMALL LETTER Z WITH DOT ABOVE
+0xC0 0x0154 # LATIN CAPITAL LETTER R WITH ACUTE
+0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x0102 # LATIN CAPITAL LETTER A WITH BREVE
+0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x0139 # LATIN CAPITAL LETTER L WITH ACUTE
+0xC6 0x0106 # LATIN CAPITAL LETTER C WITH ACUTE
+0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x010C # LATIN CAPITAL LETTER C WITH CARON
+0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK
+0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x011A # LATIN CAPITAL LETTER E WITH CARON
+0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x010E # LATIN CAPITAL LETTER D WITH CARON
+0xD0 0x0110 # LATIN CAPITAL LETTER D WITH STROKE
+0xD1 0x0143 # LATIN CAPITAL LETTER N WITH ACUTE
+0xD2 0x0147 # LATIN CAPITAL LETTER N WITH CARON
+0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x0150 # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 # MULTIPLICATION SIGN
+0xD8 0x0158 # LATIN CAPITAL LETTER R WITH CARON
+0xD9 0x016E # LATIN CAPITAL LETTER U WITH RING ABOVE
+0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x0170 # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE 0x0162 # LATIN CAPITAL LETTER T WITH CEDILLA
+0xDF 0x00DF # LATIN SMALL LETTER SHARP S
+0xE0 0x0155 # LATIN SMALL LETTER R WITH ACUTE
+0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x0103 # LATIN SMALL LETTER A WITH BREVE
+0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x013A # LATIN SMALL LETTER L WITH ACUTE
+0xE6 0x0107 # LATIN SMALL LETTER C WITH ACUTE
+0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x010D # LATIN SMALL LETTER C WITH CARON
+0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x0119 # LATIN SMALL LETTER E WITH OGONEK
+0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x011B # LATIN SMALL LETTER E WITH CARON
+0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x010F # LATIN SMALL LETTER D WITH CARON
+0xF0 0x0111 # LATIN SMALL LETTER D WITH STROKE
+0xF1 0x0144 # LATIN SMALL LETTER N WITH ACUTE
+0xF2 0x0148 # LATIN SMALL LETTER N WITH CARON
+0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x0151 # LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 # DIVISION SIGN
+0xF8 0x0159 # LATIN SMALL LETTER R WITH CARON
+0xF9 0x016F # LATIN SMALL LETTER U WITH RING ABOVE
+0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x0171 # LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE
+0xFE 0x0163 # LATIN SMALL LETTER T WITH CEDILLA
+0xFF 0x02D9 # DOT ABOVE
--- /dev/null
+#
+# Name: ISO/IEC 8859-3:1999 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-3:1999 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-3 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-3 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x0126 # LATIN CAPITAL LETTER H WITH STROKE
+0xA2 0x02D8 # BREVE
+0xA3 0x00A3 # POUND SIGN
+0xA4 0x00A4 # CURRENCY SIGN
+0xA6 0x0124 # LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x0130 # LATIN CAPITAL LETTER I WITH DOT ABOVE
+0xAA 0x015E # LATIN CAPITAL LETTER S WITH CEDILLA
+0xAB 0x011E # LATIN CAPITAL LETTER G WITH BREVE
+0xAC 0x0134 # LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0xAD 0x00AD # SOFT HYPHEN
+0xAF 0x017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x0127 # LATIN SMALL LETTER H WITH STROKE
+0xB2 0x00B2 # SUPERSCRIPT TWO
+0xB3 0x00B3 # SUPERSCRIPT THREE
+0xB4 0x00B4 # ACUTE ACCENT
+0xB5 0x00B5 # MICRO SIGN
+0xB6 0x0125 # LATIN SMALL LETTER H WITH CIRCUMFLEX
+0xB7 0x00B7 # MIDDLE DOT
+0xB8 0x00B8 # CEDILLA
+0xB9 0x0131 # LATIN SMALL LETTER DOTLESS I
+0xBA 0x015F # LATIN SMALL LETTER S WITH CEDILLA
+0xBB 0x011F # LATIN SMALL LETTER G WITH BREVE
+0xBC 0x0135 # LATIN SMALL LETTER J WITH CIRCUMFLEX
+0xBD 0x00BD # VULGAR FRACTION ONE HALF
+0xBF 0x017C # LATIN SMALL LETTER Z WITH DOT ABOVE
+0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE
+0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x010A # LATIN CAPITAL LETTER C WITH DOT ABOVE
+0xC6 0x0108 # LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE
+0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE
+0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE
+0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE
+0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x0120 # LATIN CAPITAL LETTER G WITH DOT ABOVE
+0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 # MULTIPLICATION SIGN
+0xD8 0x011C # LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE
+0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x016C # LATIN CAPITAL LETTER U WITH BREVE
+0xDE 0x015C # LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0xDF 0x00DF # LATIN SMALL LETTER SHARP S
+0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x010B # LATIN SMALL LETTER C WITH DOT ABOVE
+0xE6 0x0109 # LATIN SMALL LETTER C WITH CIRCUMFLEX
+0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE
+0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS
+0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE
+0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE
+0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x0121 # LATIN SMALL LETTER G WITH DOT ABOVE
+0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 # DIVISION SIGN
+0xF8 0x011D # LATIN SMALL LETTER G WITH CIRCUMFLEX
+0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x016D # LATIN SMALL LETTER U WITH BREVE
+0xFE 0x015D # LATIN SMALL LETTER S WITH CIRCUMFLEX
+0xFF 0x02D9 # DOT ABOVE
--- /dev/null
+#
+# Name: ISO/IEC 8859-4:1998 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-4:1998 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-4 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-4 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK
+0xA2 0x0138 # LATIN SMALL LETTER KRA
+0xA3 0x0156 # LATIN CAPITAL LETTER R WITH CEDILLA
+0xA4 0x00A4 # CURRENCY SIGN
+0xA5 0x0128 # LATIN CAPITAL LETTER I WITH TILDE
+0xA6 0x013B # LATIN CAPITAL LETTER L WITH CEDILLA
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x0160 # LATIN CAPITAL LETTER S WITH CARON
+0xAA 0x0112 # LATIN CAPITAL LETTER E WITH MACRON
+0xAB 0x0122 # LATIN CAPITAL LETTER G WITH CEDILLA
+0xAC 0x0166 # LATIN CAPITAL LETTER T WITH STROKE
+0xAD 0x00AD # SOFT HYPHEN
+0xAE 0x017D # LATIN CAPITAL LETTER Z WITH CARON
+0xAF 0x00AF # MACRON
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x0105 # LATIN SMALL LETTER A WITH OGONEK
+0xB2 0x02DB # OGONEK
+0xB3 0x0157 # LATIN SMALL LETTER R WITH CEDILLA
+0xB4 0x00B4 # ACUTE ACCENT
+0xB5 0x0129 # LATIN SMALL LETTER I WITH TILDE
+0xB6 0x013C # LATIN SMALL LETTER L WITH CEDILLA
+0xB7 0x02C7 # CARON
+0xB8 0x00B8 # CEDILLA
+0xB9 0x0161 # LATIN SMALL LETTER S WITH CARON
+0xBA 0x0113 # LATIN SMALL LETTER E WITH MACRON
+0xBB 0x0123 # LATIN SMALL LETTER G WITH CEDILLA
+0xBC 0x0167 # LATIN SMALL LETTER T WITH STROKE
+0xBD 0x014A # LATIN CAPITAL LETTER ENG
+0xBE 0x017E # LATIN SMALL LETTER Z WITH CARON
+0xBF 0x014B # LATIN SMALL LETTER ENG
+0xC0 0x0100 # LATIN CAPITAL LETTER A WITH MACRON
+0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE
+0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x00C6 # LATIN CAPITAL LETTER AE
+0xC7 0x012E # LATIN CAPITAL LETTER I WITH OGONEK
+0xC8 0x010C # LATIN CAPITAL LETTER C WITH CARON
+0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK
+0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x0116 # LATIN CAPITAL LETTER E WITH DOT ABOVE
+0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x012A # LATIN CAPITAL LETTER I WITH MACRON
+0xD0 0x0110 # LATIN CAPITAL LETTER D WITH STROKE
+0xD1 0x0145 # LATIN CAPITAL LETTER N WITH CEDILLA
+0xD2 0x014C # LATIN CAPITAL LETTER O WITH MACRON
+0xD3 0x0136 # LATIN CAPITAL LETTER K WITH CEDILLA
+0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE
+0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 # MULTIPLICATION SIGN
+0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE
+0xD9 0x0172 # LATIN CAPITAL LETTER U WITH OGONEK
+0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x0168 # LATIN CAPITAL LETTER U WITH TILDE
+0xDE 0x016A # LATIN CAPITAL LETTER U WITH MACRON
+0xDF 0x00DF # LATIN SMALL LETTER SHARP S
+0xE0 0x0101 # LATIN SMALL LETTER A WITH MACRON
+0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE
+0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x00E6 # LATIN SMALL LETTER AE
+0xE7 0x012F # LATIN SMALL LETTER I WITH OGONEK
+0xE8 0x010D # LATIN SMALL LETTER C WITH CARON
+0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x0119 # LATIN SMALL LETTER E WITH OGONEK
+0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x0117 # LATIN SMALL LETTER E WITH DOT ABOVE
+0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x012B # LATIN SMALL LETTER I WITH MACRON
+0xF0 0x0111 # LATIN SMALL LETTER D WITH STROKE
+0xF1 0x0146 # LATIN SMALL LETTER N WITH CEDILLA
+0xF2 0x014D # LATIN SMALL LETTER O WITH MACRON
+0xF3 0x0137 # LATIN SMALL LETTER K WITH CEDILLA
+0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE
+0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 # DIVISION SIGN
+0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE
+0xF9 0x0173 # LATIN SMALL LETTER U WITH OGONEK
+0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x0169 # LATIN SMALL LETTER U WITH TILDE
+0xFE 0x016B # LATIN SMALL LETTER U WITH MACRON
+0xFF 0x02D9 # DOT ABOVE
--- /dev/null
+#
+# Name: ISO 8859-5:1999 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-5:1999 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-5 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-5 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x0401 # CYRILLIC CAPITAL LETTER IO
+0xA2 0x0402 # CYRILLIC CAPITAL LETTER DJE
+0xA3 0x0403 # CYRILLIC CAPITAL LETTER GJE
+0xA4 0x0404 # CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xA5 0x0405 # CYRILLIC CAPITAL LETTER DZE
+0xA6 0x0406 # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xA7 0x0407 # CYRILLIC CAPITAL LETTER YI
+0xA8 0x0408 # CYRILLIC CAPITAL LETTER JE
+0xA9 0x0409 # CYRILLIC CAPITAL LETTER LJE
+0xAA 0x040A # CYRILLIC CAPITAL LETTER NJE
+0xAB 0x040B # CYRILLIC CAPITAL LETTER TSHE
+0xAC 0x040C # CYRILLIC CAPITAL LETTER KJE
+0xAD 0x00AD # SOFT HYPHEN
+0xAE 0x040E # CYRILLIC CAPITAL LETTER SHORT U
+0xAF 0x040F # CYRILLIC CAPITAL LETTER DZHE
+0xB0 0x0410 # CYRILLIC CAPITAL LETTER A
+0xB1 0x0411 # CYRILLIC CAPITAL LETTER BE
+0xB2 0x0412 # CYRILLIC CAPITAL LETTER VE
+0xB3 0x0413 # CYRILLIC CAPITAL LETTER GHE
+0xB4 0x0414 # CYRILLIC CAPITAL LETTER DE
+0xB5 0x0415 # CYRILLIC CAPITAL LETTER IE
+0xB6 0x0416 # CYRILLIC CAPITAL LETTER ZHE
+0xB7 0x0417 # CYRILLIC CAPITAL LETTER ZE
+0xB8 0x0418 # CYRILLIC CAPITAL LETTER I
+0xB9 0x0419 # CYRILLIC CAPITAL LETTER SHORT I
+0xBA 0x041A # CYRILLIC CAPITAL LETTER KA
+0xBB 0x041B # CYRILLIC CAPITAL LETTER EL
+0xBC 0x041C # CYRILLIC CAPITAL LETTER EM
+0xBD 0x041D # CYRILLIC CAPITAL LETTER EN
+0xBE 0x041E # CYRILLIC CAPITAL LETTER O
+0xBF 0x041F # CYRILLIC CAPITAL LETTER PE
+0xC0 0x0420 # CYRILLIC CAPITAL LETTER ER
+0xC1 0x0421 # CYRILLIC CAPITAL LETTER ES
+0xC2 0x0422 # CYRILLIC CAPITAL LETTER TE
+0xC3 0x0423 # CYRILLIC CAPITAL LETTER U
+0xC4 0x0424 # CYRILLIC CAPITAL LETTER EF
+0xC5 0x0425 # CYRILLIC CAPITAL LETTER HA
+0xC6 0x0426 # CYRILLIC CAPITAL LETTER TSE
+0xC7 0x0427 # CYRILLIC CAPITAL LETTER CHE
+0xC8 0x0428 # CYRILLIC CAPITAL LETTER SHA
+0xC9 0x0429 # CYRILLIC CAPITAL LETTER SHCHA
+0xCA 0x042A # CYRILLIC CAPITAL LETTER HARD SIGN
+0xCB 0x042B # CYRILLIC CAPITAL LETTER YERU
+0xCC 0x042C # CYRILLIC CAPITAL LETTER SOFT SIGN
+0xCD 0x042D # CYRILLIC CAPITAL LETTER E
+0xCE 0x042E # CYRILLIC CAPITAL LETTER YU
+0xCF 0x042F # CYRILLIC CAPITAL LETTER YA
+0xD0 0x0430 # CYRILLIC SMALL LETTER A
+0xD1 0x0431 # CYRILLIC SMALL LETTER BE
+0xD2 0x0432 # CYRILLIC SMALL LETTER VE
+0xD3 0x0433 # CYRILLIC SMALL LETTER GHE
+0xD4 0x0434 # CYRILLIC SMALL LETTER DE
+0xD5 0x0435 # CYRILLIC SMALL LETTER IE
+0xD6 0x0436 # CYRILLIC SMALL LETTER ZHE
+0xD7 0x0437 # CYRILLIC SMALL LETTER ZE
+0xD8 0x0438 # CYRILLIC SMALL LETTER I
+0xD9 0x0439 # CYRILLIC SMALL LETTER SHORT I
+0xDA 0x043A # CYRILLIC SMALL LETTER KA
+0xDB 0x043B # CYRILLIC SMALL LETTER EL
+0xDC 0x043C # CYRILLIC SMALL LETTER EM
+0xDD 0x043D # CYRILLIC SMALL LETTER EN
+0xDE 0x043E # CYRILLIC SMALL LETTER O
+0xDF 0x043F # CYRILLIC SMALL LETTER PE
+0xE0 0x0440 # CYRILLIC SMALL LETTER ER
+0xE1 0x0441 # CYRILLIC SMALL LETTER ES
+0xE2 0x0442 # CYRILLIC SMALL LETTER TE
+0xE3 0x0443 # CYRILLIC SMALL LETTER U
+0xE4 0x0444 # CYRILLIC SMALL LETTER EF
+0xE5 0x0445 # CYRILLIC SMALL LETTER HA
+0xE6 0x0446 # CYRILLIC SMALL LETTER TSE
+0xE7 0x0447 # CYRILLIC SMALL LETTER CHE
+0xE8 0x0448 # CYRILLIC SMALL LETTER SHA
+0xE9 0x0449 # CYRILLIC SMALL LETTER SHCHA
+0xEA 0x044A # CYRILLIC SMALL LETTER HARD SIGN
+0xEB 0x044B # CYRILLIC SMALL LETTER YERU
+0xEC 0x044C # CYRILLIC SMALL LETTER SOFT SIGN
+0xED 0x044D # CYRILLIC SMALL LETTER E
+0xEE 0x044E # CYRILLIC SMALL LETTER YU
+0xEF 0x044F # CYRILLIC SMALL LETTER YA
+0xF0 0x2116 # NUMERO SIGN
+0xF1 0x0451 # CYRILLIC SMALL LETTER IO
+0xF2 0x0452 # CYRILLIC SMALL LETTER DJE
+0xF3 0x0453 # CYRILLIC SMALL LETTER GJE
+0xF4 0x0454 # CYRILLIC SMALL LETTER UKRAINIAN IE
+0xF5 0x0455 # CYRILLIC SMALL LETTER DZE
+0xF6 0x0456 # CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xF7 0x0457 # CYRILLIC SMALL LETTER YI
+0xF8 0x0458 # CYRILLIC SMALL LETTER JE
+0xF9 0x0459 # CYRILLIC SMALL LETTER LJE
+0xFA 0x045A # CYRILLIC SMALL LETTER NJE
+0xFB 0x045B # CYRILLIC SMALL LETTER TSHE
+0xFC 0x045C # CYRILLIC SMALL LETTER KJE
+0xFD 0x00A7 # SECTION SIGN
+0xFE 0x045E # CYRILLIC SMALL LETTER SHORT U
+0xFF 0x045F # CYRILLIC SMALL LETTER DZHE
--- /dev/null
+#
+# Name: ISO 8859-6:1999 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-6:1999 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-6 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-6 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+# 0x30..0x39 remapped to the ASCII digits (U+0030..U+0039) instead
+# of the Arabic digits (U+0660..U+0669).
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA4 0x00A4 # CURRENCY SIGN
+0xAC 0x060C # ARABIC COMMA
+0xAD 0x00AD # SOFT HYPHEN
+0xBB 0x061B # ARABIC SEMICOLON
+0xBF 0x061F # ARABIC QUESTION MARK
+0xC1 0x0621 # ARABIC LETTER HAMZA
+0xC2 0x0622 # ARABIC LETTER ALEF WITH MADDA ABOVE
+0xC3 0x0623 # ARABIC LETTER ALEF WITH HAMZA ABOVE
+0xC4 0x0624 # ARABIC LETTER WAW WITH HAMZA ABOVE
+0xC5 0x0625 # ARABIC LETTER ALEF WITH HAMZA BELOW
+0xC6 0x0626 # ARABIC LETTER YEH WITH HAMZA ABOVE
+0xC7 0x0627 # ARABIC LETTER ALEF
+0xC8 0x0628 # ARABIC LETTER BEH
+0xC9 0x0629 # ARABIC LETTER TEH MARBUTA
+0xCA 0x062A # ARABIC LETTER TEH
+0xCB 0x062B # ARABIC LETTER THEH
+0xCC 0x062C # ARABIC LETTER JEEM
+0xCD 0x062D # ARABIC LETTER HAH
+0xCE 0x062E # ARABIC LETTER KHAH
+0xCF 0x062F # ARABIC LETTER DAL
+0xD0 0x0630 # ARABIC LETTER THAL
+0xD1 0x0631 # ARABIC LETTER REH
+0xD2 0x0632 # ARABIC LETTER ZAIN
+0xD3 0x0633 # ARABIC LETTER SEEN
+0xD4 0x0634 # ARABIC LETTER SHEEN
+0xD5 0x0635 # ARABIC LETTER SAD
+0xD6 0x0636 # ARABIC LETTER DAD
+0xD7 0x0637 # ARABIC LETTER TAH
+0xD8 0x0638 # ARABIC LETTER ZAH
+0xD9 0x0639 # ARABIC LETTER AIN
+0xDA 0x063A # ARABIC LETTER GHAIN
+0xE0 0x0640 # ARABIC TATWEEL
+0xE1 0x0641 # ARABIC LETTER FEH
+0xE2 0x0642 # ARABIC LETTER QAF
+0xE3 0x0643 # ARABIC LETTER KAF
+0xE4 0x0644 # ARABIC LETTER LAM
+0xE5 0x0645 # ARABIC LETTER MEEM
+0xE6 0x0646 # ARABIC LETTER NOON
+0xE7 0x0647 # ARABIC LETTER HEH
+0xE8 0x0648 # ARABIC LETTER WAW
+0xE9 0x0649 # ARABIC LETTER ALEF MAKSURA
+0xEA 0x064A # ARABIC LETTER YEH
+0xEB 0x064B # ARABIC FATHATAN
+0xEC 0x064C # ARABIC DAMMATAN
+0xED 0x064D # ARABIC KASRATAN
+0xEE 0x064E # ARABIC FATHA
+0xEF 0x064F # ARABIC DAMMA
+0xF0 0x0650 # ARABIC KASRA
+0xF1 0x0651 # ARABIC SHADDA
+0xF2 0x0652 # ARABIC SUKUN
--- /dev/null
+#
+# Name: ISO 8859-7:2003 to Unicode
+# Unicode version: 4.0
+# Table version: 2.0
+# Table format: Format A
+# Date: 2003-Nov-12
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-2003 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO 8859-7:2003 characters map into Unicode.
+#
+# ISO 8859-7:1987 is equivalent to ISO-IR-126, ELOT 928,
+# and ECMA 118. ISO 8859-7:2003 adds two currency signs
+# and one other character not in the earlier standard.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO 8859-7 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO 8859-7 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+# Remap 0xA1 to U+2018 (instead of 0x02BD) to match text of 8859-7
+# Remap 0xA2 to U+2019 (instead of 0x02BC) to match text of 8859-7
+#
+# 2.0 version updates 1.0 version by adding mappings for the
+# three newly added characters 0xA4, 0xA5, 0xAA.
+#
+# Updated versions of this file may be found in:
+# <http://www.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact the Unicode Consortium at:
+# <http://www.unicode.org/reporting.html>
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x2018 # LEFT SINGLE QUOTATION MARK
+0xA2 0x2019 # RIGHT SINGLE QUOTATION MARK
+0xA3 0x00A3 # POUND SIGN
+0xA4 0x20AC # EURO SIGN
+0xA5 0x20AF # DRACHMA SIGN
+0xA6 0x00A6 # BROKEN BAR
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x00A9 # COPYRIGHT SIGN
+0xAA 0x037A # GREEK YPOGEGRAMMENI
+0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC # NOT SIGN
+0xAD 0x00AD # SOFT HYPHEN
+0xAF 0x2015 # HORIZONTAL BAR
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x00B1 # PLUS-MINUS SIGN
+0xB2 0x00B2 # SUPERSCRIPT TWO
+0xB3 0x00B3 # SUPERSCRIPT THREE
+0xB4 0x0384 # GREEK TONOS
+0xB5 0x0385 # GREEK DIALYTIKA TONOS
+0xB6 0x0386 # GREEK CAPITAL LETTER ALPHA WITH TONOS
+0xB7 0x00B7 # MIDDLE DOT
+0xB8 0x0388 # GREEK CAPITAL LETTER EPSILON WITH TONOS
+0xB9 0x0389 # GREEK CAPITAL LETTER ETA WITH TONOS
+0xBA 0x038A # GREEK CAPITAL LETTER IOTA WITH TONOS
+0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x038C # GREEK CAPITAL LETTER OMICRON WITH TONOS
+0xBD 0x00BD # VULGAR FRACTION ONE HALF
+0xBE 0x038E # GREEK CAPITAL LETTER UPSILON WITH TONOS
+0xBF 0x038F # GREEK CAPITAL LETTER OMEGA WITH TONOS
+0xC0 0x0390 # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0xC1 0x0391 # GREEK CAPITAL LETTER ALPHA
+0xC2 0x0392 # GREEK CAPITAL LETTER BETA
+0xC3 0x0393 # GREEK CAPITAL LETTER GAMMA
+0xC4 0x0394 # GREEK CAPITAL LETTER DELTA
+0xC5 0x0395 # GREEK CAPITAL LETTER EPSILON
+0xC6 0x0396 # GREEK CAPITAL LETTER ZETA
+0xC7 0x0397 # GREEK CAPITAL LETTER ETA
+0xC8 0x0398 # GREEK CAPITAL LETTER THETA
+0xC9 0x0399 # GREEK CAPITAL LETTER IOTA
+0xCA 0x039A # GREEK CAPITAL LETTER KAPPA
+0xCB 0x039B # GREEK CAPITAL LETTER LAMDA
+0xCC 0x039C # GREEK CAPITAL LETTER MU
+0xCD 0x039D # GREEK CAPITAL LETTER NU
+0xCE 0x039E # GREEK CAPITAL LETTER XI
+0xCF 0x039F # GREEK CAPITAL LETTER OMICRON
+0xD0 0x03A0 # GREEK CAPITAL LETTER PI
+0xD1 0x03A1 # GREEK CAPITAL LETTER RHO
+0xD3 0x03A3 # GREEK CAPITAL LETTER SIGMA
+0xD4 0x03A4 # GREEK CAPITAL LETTER TAU
+0xD5 0x03A5 # GREEK CAPITAL LETTER UPSILON
+0xD6 0x03A6 # GREEK CAPITAL LETTER PHI
+0xD7 0x03A7 # GREEK CAPITAL LETTER CHI
+0xD8 0x03A8 # GREEK CAPITAL LETTER PSI
+0xD9 0x03A9 # GREEK CAPITAL LETTER OMEGA
+0xDA 0x03AA # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0xDB 0x03AB # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0xDC 0x03AC # GREEK SMALL LETTER ALPHA WITH TONOS
+0xDD 0x03AD # GREEK SMALL LETTER EPSILON WITH TONOS
+0xDE 0x03AE # GREEK SMALL LETTER ETA WITH TONOS
+0xDF 0x03AF # GREEK SMALL LETTER IOTA WITH TONOS
+0xE0 0x03B0 # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0xE1 0x03B1 # GREEK SMALL LETTER ALPHA
+0xE2 0x03B2 # GREEK SMALL LETTER BETA
+0xE3 0x03B3 # GREEK SMALL LETTER GAMMA
+0xE4 0x03B4 # GREEK SMALL LETTER DELTA
+0xE5 0x03B5 # GREEK SMALL LETTER EPSILON
+0xE6 0x03B6 # GREEK SMALL LETTER ZETA
+0xE7 0x03B7 # GREEK SMALL LETTER ETA
+0xE8 0x03B8 # GREEK SMALL LETTER THETA
+0xE9 0x03B9 # GREEK SMALL LETTER IOTA
+0xEA 0x03BA # GREEK SMALL LETTER KAPPA
+0xEB 0x03BB # GREEK SMALL LETTER LAMDA
+0xEC 0x03BC # GREEK SMALL LETTER MU
+0xED 0x03BD # GREEK SMALL LETTER NU
+0xEE 0x03BE # GREEK SMALL LETTER XI
+0xEF 0x03BF # GREEK SMALL LETTER OMICRON
+0xF0 0x03C0 # GREEK SMALL LETTER PI
+0xF1 0x03C1 # GREEK SMALL LETTER RHO
+0xF2 0x03C2 # GREEK SMALL LETTER FINAL SIGMA
+0xF3 0x03C3 # GREEK SMALL LETTER SIGMA
+0xF4 0x03C4 # GREEK SMALL LETTER TAU
+0xF5 0x03C5 # GREEK SMALL LETTER UPSILON
+0xF6 0x03C6 # GREEK SMALL LETTER PHI
+0xF7 0x03C7 # GREEK SMALL LETTER CHI
+0xF8 0x03C8 # GREEK SMALL LETTER PSI
+0xF9 0x03C9 # GREEK SMALL LETTER OMEGA
+0xFA 0x03CA # GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0xFB 0x03CB # GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0xFC 0x03CC # GREEK SMALL LETTER OMICRON WITH TONOS
+0xFD 0x03CD # GREEK SMALL LETTER UPSILON WITH TONOS
+0xFE 0x03CE # GREEK SMALL LETTER OMEGA WITH TONOS
--- /dev/null
+#
+# Name: ISO/IEC 8859-8:1999 to Unicode
+# Unicode version: 3.0
+# Table version: 1.1
+# Table format: Format A
+# Date: 2000-Jan-03
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on optical media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-8:1999 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-8 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-8 order.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+# 1.1 version updates to the published 8859-8:1999, correcting
+# the mapping of 0xAF and adding mappings for LRM and RLM.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA2 0x00A2 # CENT SIGN
+0xA3 0x00A3 # POUND SIGN
+0xA4 0x00A4 # CURRENCY SIGN
+0xA5 0x00A5 # YEN SIGN
+0xA6 0x00A6 # BROKEN BAR
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x00A9 # COPYRIGHT SIGN
+0xAA 0x00D7 # MULTIPLICATION SIGN
+0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC # NOT SIGN
+0xAD 0x00AD # SOFT HYPHEN
+0xAE 0x00AE # REGISTERED SIGN
+0xAF 0x00AF # MACRON
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x00B1 # PLUS-MINUS SIGN
+0xB2 0x00B2 # SUPERSCRIPT TWO
+0xB3 0x00B3 # SUPERSCRIPT THREE
+0xB4 0x00B4 # ACUTE ACCENT
+0xB5 0x00B5 # MICRO SIGN
+0xB6 0x00B6 # PILCROW SIGN
+0xB7 0x00B7 # MIDDLE DOT
+0xB8 0x00B8 # CEDILLA
+0xB9 0x00B9 # SUPERSCRIPT ONE
+0xBA 0x00F7 # DIVISION SIGN
+0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC # VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD # VULGAR FRACTION ONE HALF
+0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS
+0xDF 0x2017 # DOUBLE LOW LINE
+0xE0 0x05D0 # HEBREW LETTER ALEF
+0xE1 0x05D1 # HEBREW LETTER BET
+0xE2 0x05D2 # HEBREW LETTER GIMEL
+0xE3 0x05D3 # HEBREW LETTER DALET
+0xE4 0x05D4 # HEBREW LETTER HE
+0xE5 0x05D5 # HEBREW LETTER VAV
+0xE6 0x05D6 # HEBREW LETTER ZAYIN
+0xE7 0x05D7 # HEBREW LETTER HET
+0xE8 0x05D8 # HEBREW LETTER TET
+0xE9 0x05D9 # HEBREW LETTER YOD
+0xEA 0x05DA # HEBREW LETTER FINAL KAF
+0xEB 0x05DB # HEBREW LETTER KAF
+0xEC 0x05DC # HEBREW LETTER LAMED
+0xED 0x05DD # HEBREW LETTER FINAL MEM
+0xEE 0x05DE # HEBREW LETTER MEM
+0xEF 0x05DF # HEBREW LETTER FINAL NUN
+0xF0 0x05E0 # HEBREW LETTER NUN
+0xF1 0x05E1 # HEBREW LETTER SAMEKH
+0xF2 0x05E2 # HEBREW LETTER AYIN
+0xF3 0x05E3 # HEBREW LETTER FINAL PE
+0xF4 0x05E4 # HEBREW LETTER PE
+0xF5 0x05E5 # HEBREW LETTER FINAL TSADI
+0xF6 0x05E6 # HEBREW LETTER TSADI
+0xF7 0x05E7 # HEBREW LETTER QOF
+0xF8 0x05E8 # HEBREW LETTER RESH
+0xF9 0x05E9 # HEBREW LETTER SHIN
+0xFA 0x05EA # HEBREW LETTER TAV
+0xFD 0x200E # LEFT-TO-RIGHT MARK
+0xFE 0x200F # RIGHT-TO-LEFT MARK
+
--- /dev/null
+#
+# Name: ISO/IEC 8859-9:1999 to Unicode
+# Unicode version: 3.0
+# Table version: 1.0
+# Table format: Format A
+# Date: 1999 July 27
+# Authors: Ken Whistler <kenw@sybase.com>
+#
+# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
+#
+# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+# No claims are made as to fitness for any particular purpose. No
+# warranties of any kind are expressed or implied. The recipient
+# agrees to determine applicability of information provided. If this
+# file has been provided on magnetic media by Unicode, Inc., the sole
+# remedy for any claim will be exchange of defective media within 90
+# days of receipt.
+#
+# Unicode, Inc. hereby grants the right to freely use the information
+# supplied in this file in the creation of products supporting the
+# Unicode Standard, and to make copies of this file in any form for
+# internal or external distribution as long as this notice remains
+# attached.
+#
+# General notes:
+#
+# This table contains the data the Unicode Consortium has on how
+# ISO/IEC 8859-9:1999 characters map into Unicode.
+#
+# Format: Three tab-separated columns
+# Column #1 is the ISO/IEC 8859-9 code (in hex as 0xXX)
+# Column #2 is the Unicode (in hex as 0xXXXX)
+# Column #3 the Unicode name (follows a comment sign, '#')
+#
+# The entries are in ISO/IEC 8859-9 order.
+#
+# ISO/IEC 8859-9 is also equivalent to ISO-IR-148.
+#
+# Version history
+# 1.0 version updates 0.1 version by adding mappings for all
+# control characters.
+#
+# Updated versions of this file may be found in:
+# <ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+# Any comments or problems, contact <errata@unicode.org>
+# Please note that <errata@unicode.org> is an archival address;
+# notices will be checked, but do not expect an immediate response.
+#
+0x00 0x0000 # NULL
+0x01 0x0001 # START OF HEADING
+0x02 0x0002 # START OF TEXT
+0x03 0x0003 # END OF TEXT
+0x04 0x0004 # END OF TRANSMISSION
+0x05 0x0005 # ENQUIRY
+0x06 0x0006 # ACKNOWLEDGE
+0x07 0x0007 # BELL
+0x08 0x0008 # BACKSPACE
+0x09 0x0009 # HORIZONTAL TABULATION
+0x0A 0x000A # LINE FEED
+0x0B 0x000B # VERTICAL TABULATION
+0x0C 0x000C # FORM FEED
+0x0D 0x000D # CARRIAGE RETURN
+0x0E 0x000E # SHIFT OUT
+0x0F 0x000F # SHIFT IN
+0x10 0x0010 # DATA LINK ESCAPE
+0x11 0x0011 # DEVICE CONTROL ONE
+0x12 0x0012 # DEVICE CONTROL TWO
+0x13 0x0013 # DEVICE CONTROL THREE
+0x14 0x0014 # DEVICE CONTROL FOUR
+0x15 0x0015 # NEGATIVE ACKNOWLEDGE
+0x16 0x0016 # SYNCHRONOUS IDLE
+0x17 0x0017 # END OF TRANSMISSION BLOCK
+0x18 0x0018 # CANCEL
+0x19 0x0019 # END OF MEDIUM
+0x1A 0x001A # SUBSTITUTE
+0x1B 0x001B # ESCAPE
+0x1C 0x001C # FILE SEPARATOR
+0x1D 0x001D # GROUP SEPARATOR
+0x1E 0x001E # RECORD SEPARATOR
+0x1F 0x001F # UNIT SEPARATOR
+0x20 0x0020 # SPACE
+0x21 0x0021 # EXCLAMATION MARK
+0x22 0x0022 # QUOTATION MARK
+0x23 0x0023 # NUMBER SIGN
+0x24 0x0024 # DOLLAR SIGN
+0x25 0x0025 # PERCENT SIGN
+0x26 0x0026 # AMPERSAND
+0x27 0x0027 # APOSTROPHE
+0x28 0x0028 # LEFT PARENTHESIS
+0x29 0x0029 # RIGHT PARENTHESIS
+0x2A 0x002A # ASTERISK
+0x2B 0x002B # PLUS SIGN
+0x2C 0x002C # COMMA
+0x2D 0x002D # HYPHEN-MINUS
+0x2E 0x002E # FULL STOP
+0x2F 0x002F # SOLIDUS
+0x30 0x0030 # DIGIT ZERO
+0x31 0x0031 # DIGIT ONE
+0x32 0x0032 # DIGIT TWO
+0x33 0x0033 # DIGIT THREE
+0x34 0x0034 # DIGIT FOUR
+0x35 0x0035 # DIGIT FIVE
+0x36 0x0036 # DIGIT SIX
+0x37 0x0037 # DIGIT SEVEN
+0x38 0x0038 # DIGIT EIGHT
+0x39 0x0039 # DIGIT NINE
+0x3A 0x003A # COLON
+0x3B 0x003B # SEMICOLON
+0x3C 0x003C # LESS-THAN SIGN
+0x3D 0x003D # EQUALS SIGN
+0x3E 0x003E # GREATER-THAN SIGN
+0x3F 0x003F # QUESTION MARK
+0x40 0x0040 # COMMERCIAL AT
+0x41 0x0041 # LATIN CAPITAL LETTER A
+0x42 0x0042 # LATIN CAPITAL LETTER B
+0x43 0x0043 # LATIN CAPITAL LETTER C
+0x44 0x0044 # LATIN CAPITAL LETTER D
+0x45 0x0045 # LATIN CAPITAL LETTER E
+0x46 0x0046 # LATIN CAPITAL LETTER F
+0x47 0x0047 # LATIN CAPITAL LETTER G
+0x48 0x0048 # LATIN CAPITAL LETTER H
+0x49 0x0049 # LATIN CAPITAL LETTER I
+0x4A 0x004A # LATIN CAPITAL LETTER J
+0x4B 0x004B # LATIN CAPITAL LETTER K
+0x4C 0x004C # LATIN CAPITAL LETTER L
+0x4D 0x004D # LATIN CAPITAL LETTER M
+0x4E 0x004E # LATIN CAPITAL LETTER N
+0x4F 0x004F # LATIN CAPITAL LETTER O
+0x50 0x0050 # LATIN CAPITAL LETTER P
+0x51 0x0051 # LATIN CAPITAL LETTER Q
+0x52 0x0052 # LATIN CAPITAL LETTER R
+0x53 0x0053 # LATIN CAPITAL LETTER S
+0x54 0x0054 # LATIN CAPITAL LETTER T
+0x55 0x0055 # LATIN CAPITAL LETTER U
+0x56 0x0056 # LATIN CAPITAL LETTER V
+0x57 0x0057 # LATIN CAPITAL LETTER W
+0x58 0x0058 # LATIN CAPITAL LETTER X
+0x59 0x0059 # LATIN CAPITAL LETTER Y
+0x5A 0x005A # LATIN CAPITAL LETTER Z
+0x5B 0x005B # LEFT SQUARE BRACKET
+0x5C 0x005C # REVERSE SOLIDUS
+0x5D 0x005D # RIGHT SQUARE BRACKET
+0x5E 0x005E # CIRCUMFLEX ACCENT
+0x5F 0x005F # LOW LINE
+0x60 0x0060 # GRAVE ACCENT
+0x61 0x0061 # LATIN SMALL LETTER A
+0x62 0x0062 # LATIN SMALL LETTER B
+0x63 0x0063 # LATIN SMALL LETTER C
+0x64 0x0064 # LATIN SMALL LETTER D
+0x65 0x0065 # LATIN SMALL LETTER E
+0x66 0x0066 # LATIN SMALL LETTER F
+0x67 0x0067 # LATIN SMALL LETTER G
+0x68 0x0068 # LATIN SMALL LETTER H
+0x69 0x0069 # LATIN SMALL LETTER I
+0x6A 0x006A # LATIN SMALL LETTER J
+0x6B 0x006B # LATIN SMALL LETTER K
+0x6C 0x006C # LATIN SMALL LETTER L
+0x6D 0x006D # LATIN SMALL LETTER M
+0x6E 0x006E # LATIN SMALL LETTER N
+0x6F 0x006F # LATIN SMALL LETTER O
+0x70 0x0070 # LATIN SMALL LETTER P
+0x71 0x0071 # LATIN SMALL LETTER Q
+0x72 0x0072 # LATIN SMALL LETTER R
+0x73 0x0073 # LATIN SMALL LETTER S
+0x74 0x0074 # LATIN SMALL LETTER T
+0x75 0x0075 # LATIN SMALL LETTER U
+0x76 0x0076 # LATIN SMALL LETTER V
+0x77 0x0077 # LATIN SMALL LETTER W
+0x78 0x0078 # LATIN SMALL LETTER X
+0x79 0x0079 # LATIN SMALL LETTER Y
+0x7A 0x007A # LATIN SMALL LETTER Z
+0x7B 0x007B # LEFT CURLY BRACKET
+0x7C 0x007C # VERTICAL LINE
+0x7D 0x007D # RIGHT CURLY BRACKET
+0x7E 0x007E # TILDE
+0x7F 0x007F # DELETE
+0x80 0x0080 # <control>
+0x81 0x0081 # <control>
+0x82 0x0082 # <control>
+0x83 0x0083 # <control>
+0x84 0x0084 # <control>
+0x85 0x0085 # <control>
+0x86 0x0086 # <control>
+0x87 0x0087 # <control>
+0x88 0x0088 # <control>
+0x89 0x0089 # <control>
+0x8A 0x008A # <control>
+0x8B 0x008B # <control>
+0x8C 0x008C # <control>
+0x8D 0x008D # <control>
+0x8E 0x008E # <control>
+0x8F 0x008F # <control>
+0x90 0x0090 # <control>
+0x91 0x0091 # <control>
+0x92 0x0092 # <control>
+0x93 0x0093 # <control>
+0x94 0x0094 # <control>
+0x95 0x0095 # <control>
+0x96 0x0096 # <control>
+0x97 0x0097 # <control>
+0x98 0x0098 # <control>
+0x99 0x0099 # <control>
+0x9A 0x009A # <control>
+0x9B 0x009B # <control>
+0x9C 0x009C # <control>
+0x9D 0x009D # <control>
+0x9E 0x009E # <control>
+0x9F 0x009F # <control>
+0xA0 0x00A0 # NO-BREAK SPACE
+0xA1 0x00A1 # INVERTED EXCLAMATION MARK
+0xA2 0x00A2 # CENT SIGN
+0xA3 0x00A3 # POUND SIGN
+0xA4 0x00A4 # CURRENCY SIGN
+0xA5 0x00A5 # YEN SIGN
+0xA6 0x00A6 # BROKEN BAR
+0xA7 0x00A7 # SECTION SIGN
+0xA8 0x00A8 # DIAERESIS
+0xA9 0x00A9 # COPYRIGHT SIGN
+0xAA 0x00AA # FEMININE ORDINAL INDICATOR
+0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC 0x00AC # NOT SIGN
+0xAD 0x00AD # SOFT HYPHEN
+0xAE 0x00AE # REGISTERED SIGN
+0xAF 0x00AF # MACRON
+0xB0 0x00B0 # DEGREE SIGN
+0xB1 0x00B1 # PLUS-MINUS SIGN
+0xB2 0x00B2 # SUPERSCRIPT TWO
+0xB3 0x00B3 # SUPERSCRIPT THREE
+0xB4 0x00B4 # ACUTE ACCENT
+0xB5 0x00B5 # MICRO SIGN
+0xB6 0x00B6 # PILCROW SIGN
+0xB7 0x00B7 # MIDDLE DOT
+0xB8 0x00B8 # CEDILLA
+0xB9 0x00B9 # SUPERSCRIPT ONE
+0xBA 0x00BA # MASCULINE ORDINAL INDICATOR
+0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC 0x00BC # VULGAR FRACTION ONE QUARTER
+0xBD 0x00BD # VULGAR FRACTION ONE HALF
+0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS
+0xBF 0x00BF # INVERTED QUESTION MARK
+0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE
+0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE
+0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE
+0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6 0x00C6 # LATIN CAPITAL LETTER AE
+0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE
+0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE
+0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE
+0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE
+0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0 0x011E # LATIN CAPITAL LETTER G WITH BREVE
+0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE
+0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE
+0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE
+0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE
+0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7 0x00D7 # MULTIPLICATION SIGN
+0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE
+0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE
+0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE
+0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD 0x0130 # LATIN CAPITAL LETTER I WITH DOT ABOVE
+0xDE 0x015E # LATIN CAPITAL LETTER S WITH CEDILLA
+0xDF 0x00DF # LATIN SMALL LETTER SHARP S
+0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE
+0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE
+0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE
+0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS
+0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE
+0xE6 0x00E6 # LATIN SMALL LETTER AE
+0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA
+0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE
+0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE
+0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS
+0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE
+0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE
+0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS
+0xF0 0x011F # LATIN SMALL LETTER G WITH BREVE
+0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE
+0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE
+0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE
+0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE
+0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS
+0xF7 0x00F7 # DIVISION SIGN
+0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE
+0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE
+0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE
+0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS
+0xFD 0x0131 # LATIN SMALL LETTER DOTLESS I
+0xFE 0x015F # LATIN SMALL LETTER S WITH CEDILLA
+0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS
+
+
--- /dev/null
+<?php
+/*
+ File: read_enriched.inc
+ Author: Ryo Chijiiwa
+ License: GPL (part of IlohaMail)
+ Purpose: functions for handling text/enriched messages
+ Reference: RFC 1523, 1896
+*/
+
+
+function enriched_convert_newlines($str){
+ //remove single newlines, convert N newlines to N-1
+
+ $str = str_replace("\r\n","\n",$str);
+ $len = strlen($str);
+
+ $nl = 0;
+ $out = '';
+ for($i=0;$i<$len;$i++){
+ $c = $str[$i];
+ if (ord($c)==10) $nl++;
+ if ($nl && ord($c)!=10) $nl = 0;
+ if ($nl!=1) $out.=$c;
+ else $out.=' ';
+ }
+ return $out;
+}
+
+function enriched_convert_formatting($body){
+ $a=array('<bold>'=>'<b>','</bold>'=>'</b>','<italic>'=>'<i>',
+ '</italic>'=>'</i>','<fixed>'=>'<tt>','</fixed>'=>'</tt>',
+ '<smaller>'=>'<font size=-1>','</smaller>'=>'</font>',
+ '<bigger>'=>'<font size=+1>','</bigger>'=>'</font>',
+ '<underline>'=>'<span style="text-decoration: underline">',
+ '</underline>'=>'</span>',
+ '<flushleft>'=>'<span style="text-align:left">',
+ '</flushleft>'=>'</span>',
+ '<flushright>'=>'<span style="text-align:right">',
+ '</flushright>'=>'</span>',
+ '<flushboth>'=>'<span style="text-align:justified">',
+ '</flushboth>'=>'</span>',
+ '<indent>'=>'<span style="padding-left: 20px">',
+ '</indent>'=>'</span>',
+ '<indentright>'=>'<span style="padding-right: 20px">',
+ '</indentright>'=>'</span>');
+
+ while(list($find,$replace)=each($a)){
+ $body = eregi_replace($find,$replace,$body);
+ }
+ return $body;
+}
+
+function enriched_font($body){
+ $pattern = '/(.*)\<fontfamily\>\<param\>(.*)\<\/param\>(.*)\<\/fontfamily\>(.*)/ims';
+ while(preg_match($pattern,$body,$a)){
+ //print_r($a);
+ if (count($a)!=5) continue;
+ $body=$a[1].'<span style="font-family: '.$a[2].'">'.$a[3].'</span>'.$a[4];
+ }
+
+ return $body;
+}
+
+
+function enriched_color($body){
+ $pattern = '/(.*)\<color\>\<param\>(.*)\<\/param\>(.*)\<\/color\>(.*)/ims';
+ while(preg_match($pattern,$body,$a)){
+ //print_r($a);
+ if (count($a)!=5) continue;
+
+ //extract color (either by name, or ####,####,####)
+ if (strpos($a[2],',')){
+ $rgb = explode(',',$a[2]);
+ $color ='#';
+ for($i=0;$i<3;$i++) $color.=substr($rgb[$i],0,2); //just take first 2 bytes
+ }else{
+ $color = $a[2];
+ }
+
+ //put it all together
+ $body = $a[1].'<span style="color: '.$color.'">'.$a[3].'</span>'.$a[4];
+ }
+
+ return $body;
+}
+
+function enriched_excerpt($body){
+
+ $pattern = '/(.*)\<excerpt\>(.*)\<\/excerpt\>(.*)/i';
+ while(preg_match($pattern,$body,$a)){
+ //print_r($a);
+ if (count($a)!=4) continue;
+ $quoted = '';
+ $lines = explode('<br>',$a[2]);
+ foreach($lines as $n=>$line) $quoted.='>'.$line.'<br>';
+ $body=$a[1].'<span class="quotes">'.$quoted.'</span>'.$a[3];
+ }
+
+ return $body;
+}
+
+function enriched_to_html($body){
+ $body = str_replace('<<','<',$body);
+ $body = enriched_convert_newlines($body);
+ $body = str_replace("\n", '<br>', $body);
+ $body = enriched_convert_formatting($body);
+ $body = enriched_color($body);
+ $body = enriched_font($body);
+ $body = enriched_excerpt($body);
+ //$body = nl2br($body);
+ return $body;
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*************************************************************************
+* *
+* class.html2text.inc *
+* *
+*************************************************************************
+* *
+* Converts HTML to formatted plain text *
+* *
+* Copyright (c) 2005 Jon Abernathy <jon@chuggnutt.com> *
+* All rights reserved. *
+* *
+* This script is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* The GNU General Public License can be found at *
+* http://www.gnu.org/copyleft/gpl.html. *
+* *
+* This script is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* Author(s): Jon Abernathy <jon@chuggnutt.com> *
+* *
+* Last modified: 04/06/05 *
+* Modified: 2004/05/19 (tbr) *
+* *
+*************************************************************************/
+
+
+/**
+* Takes HTML and converts it to formatted, plain text.
+*
+* Thanks to Alexander Krug (http://www.krugar.de/) to pointing out and
+* correcting an error in the regexp search array. Fixed 7/30/03.
+*
+* Updated set_html() function's file reading mechanism, 9/25/03.
+*
+* Thanks to Joss Sanglier (http://www.dancingbear.co.uk/) for adding
+* several more HTML entity codes to the $search and $replace arrays.
+* Updated 11/7/03.
+*
+* Thanks to Darius Kasperavicius (http://www.dar.dar.lt/) for
+* suggesting the addition of $allowed_tags and its supporting function
+* (which I slightly modified). Updated 3/12/04.
+*
+* Thanks to Justin Dearing for pointing out that a replacement for the
+* <TH> tag was missing, and suggesting an appropriate fix.
+* Updated 8/25/04.
+*
+* Thanks to Mathieu Collas (http://www.myefarm.com/) for finding a
+* display/formatting bug in the _build_link_list() function: email
+* readers would show the left bracket and number ("[1") as part of the
+* rendered email address.
+* Updated 12/16/04.
+*
+* Thanks to Wojciech Bajon (http://histeria.pl/) for submitting code
+* to handle relative links, which I hadn't considered. I modified his
+* code a bit to handle normal HTTP links and MAILTO links. Also for
+* suggesting three additional HTML entity codes to search for.
+* Updated 03/02/05.
+*
+* Thanks to Jacob Chandler for pointing out another link condition
+* for the _build_link_list() function: "https".
+* Updated 04/06/05.
+*
+* @author Jon Abernathy <jon@chuggnutt.com>
+* @version 0.6.1
+* @since PHP 4.0.2
+*/
+class html2text
+{
+
+ /**
+ * Contains the HTML content to convert.
+ *
+ * @var string $html
+ * @access public
+ */
+ var $html;
+
+ /**
+ * Contains the converted, formatted text.
+ *
+ * @var string $text
+ * @access public
+ */
+ var $text;
+
+ /**
+ * Maximum width of the formatted text, in columns.
+ *
+ * @var integer $width
+ * @access public
+ */
+ var $width = 70;
+
+ /**
+ * List of preg* regular expression patterns to search for,
+ * used in conjunction with $replace.
+ *
+ * @var array $search
+ * @access public
+ * @see $replace
+ */
+ var $search = array(
+ "/\r/", // Non-legal carriage return
+ "/[\n\t]+/", // Newlines and tabs
+ '/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with
+ //'/<!-- .* -->/', // Comments -- which strip_tags might have problem a with
+ '/<a href="([^"]+)"[^>]*>(.+?)<\/a>/ie', // <a href="">
+ '/<h[123][^>]*>(.+?)<\/h[123]>/ie', // H1 - H3
+ '/<h[456][^>]*>(.+?)<\/h[456]>/ie', // H4 - H6
+ '/<p[^>]*>/i', // <P>
+ '/<br[^>]*>/i', // <br>
+ '/<b[^>]*>(.+?)<\/b>/ie', // <b>
+ '/<i[^>]*>(.+?)<\/i>/i', // <i>
+ '/(<ul[^>]*>|<\/ul>)/i', // <ul> and </ul>
+ '/(<ol[^>]*>|<\/ol>)/i', // <ol> and </ol>
+ '/<li[^>]*>/i', // <li>
+ '/<hr[^>]*>/i', // <hr>
+ '/(<table[^>]*>|<\/table>)/i', // <table> and </table>
+ '/(<tr[^>]*>|<\/tr>)/i', // <tr> and </tr>
+ '/<td[^>]*>(.+?)<\/td>/i', // <td> and </td>
+ '/<th[^>]*>(.+?)<\/th>/i', // <th> and </th>
+ '/ /i',
+ '/"/i',
+ '/>/i',
+ '/</i',
+ '/&/i',
+ '/©/i',
+ '/™/i',
+ '/“/',
+ '/”/',
+ '/–/',
+ '/’/',
+ '/&/',
+ '/©/',
+ '/™/',
+ '/—/',
+ '/“/',
+ '/”/',
+ '/•/',
+ '/®/i',
+ '/•/i',
+ '/&[&;]+;/i'
+ );
+
+ /**
+ * List of pattern replacements corresponding to patterns searched.
+ *
+ * @var array $replace
+ * @access public
+ * @see $search
+ */
+ var $replace = array(
+ '', // Non-legal carriage return
+ ' ', // Newlines and tabs
+ '', // <script>s -- which strip_tags supposedly has problems with
+ //'', // Comments -- which strip_tags might have problem a with
+ '$this->_build_link_list("\\1", "\\2")', // <a href="">
+ "strtoupper(\"\n\n\\1\n\n\")", // H1 - H3
+ "ucwords(\"\n\n\\1\n\n\")", // H4 - H6
+ "\n\n\t", // <P>
+ "\n", // <br>
+ 'strtoupper("\\1")', // <b>
+ '_\\1_', // <i>
+ "\n\n", // <ul> and </ul>
+ "\n\n", // <ol> and </ol>
+ "\t*", // <li>
+ "\n-------------------------\n", // <hr>
+ "\n\n", // <table> and </table>
+ "\n", // <tr> and </tr>
+ "\t\t\\1\n", // <td> and </td>
+ "strtoupper(\"\t\t\\1\n\")", // <th> and </th>
+ ' ',
+ '"',
+ '>',
+ '<',
+ '&',
+ '(c)',
+ '(tm)',
+ '"',
+ '"',
+ '-',
+ "'",
+ '&',
+ '(c)',
+ '(tm)',
+ '--',
+ '"',
+ '"',
+ '*',
+ '(R)',
+ '*',
+ ''
+ );
+
+ /**
+ * Contains a list of HTML tags to allow in the resulting text.
+ *
+ * @var string $allowed_tags
+ * @access public
+ * @see set_allowed_tags()
+ */
+ var $allowed_tags = '';
+
+ /**
+ * Contains the base URL that relative links should resolve to.
+ *
+ * @var string $url
+ * @access public
+ */
+ var $url;
+
+ /**
+ * Indicates whether content in the $html variable has been converted yet.
+ *
+ * @var boolean $converted
+ * @access private
+ * @see $html, $text
+ */
+ var $_converted = false;
+
+ /**
+ * Contains URL addresses from links to be rendered in plain text.
+ *
+ * @var string $link_list
+ * @access private
+ * @see _build_link_list()
+ */
+ var $_link_list = array();
+
+ /**
+ * Constructor.
+ *
+ * If the HTML source string (or file) is supplied, the class
+ * will instantiate with that source propagated, all that has
+ * to be done it to call get_text().
+ *
+ * @param string $source HTML content
+ * @param boolean $from_file Indicates $source is a file to pull content from
+ * @access public
+ * @return void
+ */
+ function html2text( $source = '', $from_file = false )
+ {
+ if ( !empty($source) ) {
+ $this->set_html($source, $from_file);
+ }
+ $this->set_base_url();
+ }
+
+ /**
+ * Loads source HTML into memory, either from $source string or a file.
+ *
+ * @param string $source HTML content
+ * @param boolean $from_file Indicates $source is a file to pull content from
+ * @access public
+ * @return void
+ */
+ function set_html( $source, $from_file = false )
+ {
+ $this->html = $source;
+
+ if ( $from_file && file_exists($source) ) {
+ $fp = fopen($source, 'r');
+ $this->html = fread($fp, filesize($source));
+ fclose($fp);
+ }
+
+ $this->_converted = false;
+ }
+
+ /**
+ * Returns the text, converted from HTML.
+ *
+ * @access public
+ * @return string
+ */
+ function get_text()
+ {
+ if ( !$this->_converted ) {
+ $this->_convert();
+ }
+
+ return $this->text;
+ }
+
+ /**
+ * Prints the text, converted from HTML.
+ *
+ * @access public
+ * @return void
+ */
+ function print_text()
+ {
+ print $this->get_text();
+ }
+
+ /**
+ * Alias to print_text(), operates identically.
+ *
+ * @access public
+ * @return void
+ * @see print_text()
+ */
+ function p()
+ {
+ print $this->get_text();
+ }
+
+ /**
+ * Sets the allowed HTML tags to pass through to the resulting text.
+ *
+ * Tags should be in the form "<p>", with no corresponding closing tag.
+ *
+ * @access public
+ * @return void
+ */
+ function set_allowed_tags( $allowed_tags = '' )
+ {
+ if ( !empty($allowed_tags) ) {
+ $this->allowed_tags = $allowed_tags;
+ }
+ }
+
+ /**
+ * Sets a base URL to handle relative links.
+ *
+ * @access public
+ * @return void
+ */
+ function set_base_url( $url = '' )
+ {
+ if ( empty($url) ) {
+ $this->url = 'http://' . $_SERVER['HTTP_HOST'];
+ } else {
+ // Strip any trailing slashes for consistency (relative
+ // URLs may already start with a slash like "/file.html")
+ if ( substr($url, -1) == '/' ) {
+ $url = substr($url, 0, -1);
+ }
+ $this->url = $url;
+ }
+ }
+
+ /**
+ * Workhorse function that does actual conversion.
+ *
+ * First performs custom tag replacement specified by $search and
+ * $replace arrays. Then strips any remaining HTML tags, reduces whitespace
+ * and newlines to a readable format, and word wraps the text to
+ * $width characters.
+ *
+ * @access private
+ * @return void
+ */
+ function _convert()
+ {
+ // Variables used for building the link list
+ //$link_count = 1;
+ //$this->_link_list = '';
+
+ $text = trim(stripslashes($this->html));
+
+ // Run our defined search-and-replace
+ $text = preg_replace($this->search, $this->replace, $text);
+
+ // Strip any other HTML tags
+ $text = strip_tags($text, $this->allowed_tags);
+
+ // Bring down number of empty lines to 2 max
+ $text = preg_replace("/\n\s+\n/", "\n", $text);
+ $text = preg_replace("/[\n]{3,}/", "\n\n", $text);
+
+ // Add link list
+ if ( sizeof($this->_link_list) ) {
+ $text .= "\n\nLinks:\n------\n";
+ foreach ($this->_link_list as $id => $link) {
+ $text .= '[' . ($id+1) . '] ' . $link . "\n";
+ }
+ }
+
+ // Wrap the text to a readable format
+ // for PHP versions >= 4.0.2. Default width is 75
+ $text = wordwrap($text, $this->width);
+
+ $this->text = $text;
+
+ $this->_converted = true;
+ }
+
+ /**
+ * Helper function called by preg_replace() on link replacement.
+ *
+ * Maintains an internal list of links to be displayed at the end of the
+ * text, with numeric indices to the original point in the text they
+ * appeared. Also makes an effort at identifying and handling absolute
+ * and relative links.
+ *
+ * @param integer $link_count Counter tracking current link number
+ * @param string $link URL of the link
+ * @param string $display Part of the text to associate number with
+ * @access private
+ * @return string
+ */
+ function _build_link_list($link, $display)
+ {
+ $link_lc = strtolower($link);
+
+ if (substr($link_lc, 0, 7) == 'http://' || substr($link_lc, 0, 8) == 'https://' || substr($link_lc, 0, 7) == 'mailto:')
+ {
+ $url = $link;
+ }
+ else
+ {
+ $url = $this->url;
+ if ($link{0} != '/') {
+ $url .= '/';
+ }
+ $url .= $link;
+ }
+
+ $index = array_search($url, $this->_link_list);
+ if ($index===FALSE)
+ {
+ $index = sizeof($this->_link_list);
+ $this->_link_list[$index] = $url;
+ }
+
+ return $display . ' [' . ($index+1) . ']';
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+function mod_b64_decode($data){
+ return base64_decode(str_replace(",","/",$data));
+}
+
+function mod_b64_encode($data){
+ return str_replace("/",",",str_replace("=","",base64_encode($data)));
+}
+
+
+function utf8_to_html($str){
+ $len = strlen($str);
+ $out = "";
+ for($i=0;$i<$len;$i+=2){
+ $val = ord($str[$i]);
+ $next_val = ord($str[$i+1]);
+ if ($val<255){
+ $out.="&#".($val*256+$next_val).";";
+ }else{
+ $out.=$str[$i].$str[$i+1];
+ }
+ }
+ return $out;
+}
+
+function iil_utf7_decode($str, $raw=false){
+ if (strpos($str, '&')===false) return $str;
+
+ $len = strlen($str);
+ $in_b64 = false;
+ $b64_data = "";
+ $out = "";
+ for ($i=0;$i<$len;$i++){
+ $char = $str[$i];
+ if ($char=='&') $in_b64 = true;
+ else if ($in_b64 && $char=='-'){
+ $in_b64 = false;
+ if ($b64_data=="") $out.="&";
+ else{
+ $dec=mod_b64_decode($b64_data);
+ $out.=($raw?$dec:utf8_to_html($dec));
+ $b64_data = "";
+ }
+ }else if ($in_b64) $b64_data.=$char;
+ else $out.=$char;
+ }
+ return $out;
+}
+
+function iil_utf7_encode($str){
+ if (!ereg("[\200-\237]",$str) && !ereg("[\241-\377]",$str))
+ return $str;
+
+ $len = strlen($str);
+
+ for ($i=0;$i<$len;$i++){
+ $val = ord($str[$i]);
+ if ($val>=224 && $val<=239){
+ $unicode = ($val-224) * 4096 + (ord($str[$i+1])-128) * 64 + (ord($str[$i+2])-128);
+ $i+=2;
+ $utf_code.=chr((int)($unicode/256)).chr($unicode%256);
+ }else if ($val>=192 && $val<=223){
+ $unicode = ($val-192) * 64 + (ord($str[$i+1])-128);
+ $i++;
+ $utf_code.=chr((int)($unicode/256)).chr($unicode%256);
+ }else{
+ if ($utf_code){
+ $out.='&'.mod_b64_encode($utf_code).'-';
+ $utf_code="";
+ }
+ if ($str[$i]=="-") $out.="&";
+ $out.=$str[$i];
+ }
+ }
+ if ($utf_code)
+ $out.='&'.mod_b64_encode($utf_code).'-';
+ return $out;
+}
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/////////////////////////////////////////////////////////
+//
+// Iloha IMAP Library (IIL)
+//
+// (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org>
+//
+// This file is part of IlohaMail. IlohaMail is free software released
+// under the GPL license. See enclosed file COPYING for details, or
+// see http://www.fsf.org/copyleft/gpl.html
+//
+/////////////////////////////////////////////////////////
+
+/********************************************************
+
+ FILE: include/imap.inc
+ PURPOSE:
+ Provide alternative IMAP library that doesn't rely on the standard
+ C-Client based version. This allows IlohaMail to function regardless
+ of whether or not the PHP build it's running on has IMAP functionality
+ built-in.
+ USEAGE:
+ Function containing "_C_" in name require connection handler to be
+ passed as one of the parameters. To obtain connection handler, use
+ iil_Connect()
+ VERSION:
+ IlohaMail-0.9-20050415
+ CHANGES:
+ File altered by Thomas Bruederli <roundcube@gmail.com>
+ to fit enhanced equirements by the RoundCube Webmail:
+ - Added list of server capabilites and check these before invoking commands
+ - Added junk flag to iilBasicHeader
+ - Enhanced error reporting on fsockopen()
+ - Additional parameter for SORT command
+ - Removed Call-time pass-by-reference because deprecated
+ - Parse charset from content-type in iil_C_FetchHeaders()
+ - Enhanced heaer sorting
+ - Pass message as reference in iil_C_Append (to save memory)
+ - Added BCC and REFERENCE to the list of headers to fetch in iil_C_FetchHeaders()
+ - Leave messageID unchanged in iil_C_FetchHeaders()
+ - Avoid stripslahes in iil_Connect()
+ - Added patch to iil_SortHeaders() by Richard Green
+ - Removed <br> from error messages (better for logging)
+ - Added patch to iil_C_Sort() enabling UID SORT commands
+ - Added function iil_C_ID2UID()
+ - Casting date parts in iil_StrToTime() to avoid mktime() warnings
+ - Also acceppt LIST responses in iil_C_ListSubscribed()
+ - Sanity check of $message_set in iil_C_FetchHeaders(), iil_C_FetchHeaderIndex(), iil_C_FetchThreadHeaders()
+ - Removed some debuggers (echo ...)
+
+********************************************************/
+
+
+// changed path to work within roundcube webmail
+include_once("lib/icl_commons.inc");
+
+
+if (!$IMAP_USE_HEADER_DATE) $IMAP_USE_INTERNAL_DATE = true;
+$IMAP_MONTHS=array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12);
+$IMAP_SERVER_TZ = date('Z');
+
+$iil_error;
+$iil_errornum;
+$iil_selected;
+
+class iilConnection{
+ var $fp;
+ var $error;
+ var $errorNum;
+ var $selected;
+ var $message;
+ var $host;
+ var $cache;
+ var $uid_cache;
+ var $do_cache;
+ var $exists;
+ var $recent;
+ var $rootdir;
+ var $delimiter;
+ var $capability = array();
+}
+
+class iilBasicHeader{
+ var $id;
+ var $uid;
+ var $subject;
+ var $from;
+ var $to;
+ var $cc;
+ var $replyto;
+ var $in_reply_to;
+ var $date;
+ var $messageID;
+ var $size;
+ var $encoding;
+ var $ctype;
+ var $flags;
+ var $timestamp;
+ var $f;
+ var $seen;
+ var $deleted;
+ var $recent;
+ var $answered;
+ var $junk;
+ var $internaldate;
+ var $is_reply;
+}
+
+
+class iilThreadHeader{
+ var $id;
+ var $sbj;
+ var $irt;
+ var $mid;
+}
+
+
+function iil_xor($string, $string2){
+ $result = "";
+ $size = strlen($string);
+ for ($i=0; $i<$size; $i++) $result .= chr(ord($string[$i]) ^ ord($string2[$i]));
+
+ return $result;
+}
+
+function iil_ReadLine($fp, $size){
+ $line="";
+ if ($fp){
+ do{
+ $buffer = fgets($fp, 2048);
+ $line.=$buffer;
+ }while($buffer[strlen($buffer)-1]!="\n");
+ }
+ return $line;
+}
+
+function iil_MultLine($fp, $line){
+ $line = chop($line);
+ if (ereg('\{[0-9]+\}$', $line)){
+ $out = "";
+ preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a);
+ $bytes = $a[2][0];
+ while(strlen($out)<$bytes){
+ $out.=chop(iil_ReadLine($fp, 1024));
+ }
+ $line = $a[1][0]."\"$out\"";
+ }
+ return $line;
+}
+
+function iil_ReadBytes($fp, $bytes){
+ $data = "";
+ $len = 0;
+ do{
+ $data.=fread($fp, $bytes-$len);
+ $len = strlen($data);
+ }while($len<$bytes);
+ return $data;
+}
+
+function iil_ReadReply($fp){
+ do{
+ $line = chop(trim(iil_ReadLine($fp, 1024)));
+ }while($line[0]=="*");
+
+ return $line;
+}
+
+function iil_ParseResult($string){
+ $a=explode(" ", $string);
+ if (count($a) > 2){
+ if (strcasecmp($a[1], "OK")==0) return 0;
+ else if (strcasecmp($a[1], "NO")==0) return -1;
+ else if (strcasecmp($a[1], "BAD")==0) return -2;
+ }else return -3;
+}
+
+// check if $string starts with $match
+function iil_StartsWith($string, $match){
+ $len = strlen($match);
+ if ($len==0) return false;
+ if (strncmp($string, $match, $len)==0) return true;
+ else return false;
+}
+
+function iil_StartsWithI($string, $match){
+ $len = strlen($match);
+ if ($len==0) return false;
+ if (strncasecmp($string, $match, $len)==0) return true;
+ else return false;
+}
+
+
+function iil_C_Authenticate(&$conn, $user, $pass, $encChallenge){
+
+ // initialize ipad, opad
+ for ($i=0;$i<64;$i++){
+ $ipad.=chr(0x36);
+ $opad.=chr(0x5C);
+ }
+ // pad $pass so it's 64 bytes
+ $padLen = 64 - strlen($pass);
+ for ($i=0;$i<$padLen;$i++) $pass .= chr(0);
+ // generate hash
+ $hash = md5(iil_xor($pass,$opad).pack("H*",md5(iil_xor($pass, $ipad).base64_decode($encChallenge))));
+ // generate reply
+ $reply = base64_encode($user." ".$hash);
+
+ // send result, get reply
+ fputs($conn->fp, $reply."\r\n");
+ $line = iil_ReadLine($conn->fp, 1024);
+
+ // process result
+ if (iil_ParseResult($line)==0){
+ $conn->error .= "";
+ $conn->errorNum = 0;
+ return $conn->fp;
+ }else{
+ $conn->error .= 'Authentication for '.$user.' failed (AUTH): "'.htmlspecialchars($line)."\"";
+ $conn->errorNum = -2;
+ return false;
+ }
+}
+
+function iil_C_Login(&$conn, $user, $password){
+
+ fputs($conn->fp, "a001 LOGIN $user \"$password\"\r\n");
+
+ do{
+ $line = iil_ReadReply($conn->fp);
+ }while(!iil_StartsWith($line, "a001 "));
+ $a=explode(" ", $line);
+ if (strcmp($a[1],"OK")==0){
+ $result=$conn->fp;
+ $conn->error.="";
+ $conn->errorNum = 0;
+ }else{
+ $result=false;
+ fclose($conn->fp);
+ $conn->error .= 'Authentication for '.$user.' failed (LOGIN): "'.htmlspecialchars($line)."\"";
+ $conn->errorNum = -2;
+ }
+ return $result;
+}
+
+function iil_ParseNamespace2($str, &$i, $len=0, $l){
+ if (!$l) $str = str_replace("NIL", "()", $str);
+ if (!$len) $len = strlen($str);
+ $data = array();
+ $in_quotes = false;
+ $elem = 0;
+ for($i;$i<$len;$i++){
+ $c = (string)$str[$i];
+ if ($c=='(' && !$in_quotes){
+ $i++;
+ $data[$elem] = iil_ParseNamespace2($str, $i, $len, $l++);
+ $elem++;
+ }else if ($c==')' && !$in_quotes) return $data;
+ else if ($c=="\\"){
+ $i++;
+ if ($in_quotes) $data[$elem].=$c.$str[$i];
+ }else if ($c=='"'){
+ $in_quotes = !$in_quotes;
+ if (!$in_quotes) $elem++;
+ }else if ($in_quotes){
+ $data[$elem].=$c;
+ }
+ }
+ return $data;
+}
+
+function iil_C_NameSpace(&$conn){
+ global $my_prefs;
+
+ if (!in_array('NAMESPACE', $conn->capability))
+ return false;
+
+ if ($my_prefs["rootdir"]) return true;
+
+ fputs($conn->fp, "ns1 NAMESPACE\r\n");
+ do{
+ $line = iil_ReadLine($conn->fp, 1024);
+ if (iil_StartsWith($line, "* NAMESPACE")){
+ $i = 0;
+ $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
+ }
+ }while(!iil_StartsWith($line, "ns1"));
+
+ if (!is_array($data)) return false;
+
+ $user_space_data = $data[0];
+ if (!is_array($user_space_data)) return false;
+
+ $first_userspace = $user_space_data[0];
+ if (count($first_userspace)!=2) return false;
+
+ $conn->rootdir = $first_userspace[0];
+ $conn->delimiter = $first_userspace[1];
+ $my_prefs["rootdir"] = substr($conn->rootdir, 0, -1);
+
+ return true;
+
+}
+
+function iil_Connect($host, $user, $password){
+ global $iil_error, $iil_errornum;
+ global $ICL_SSL, $ICL_PORT;
+ global $IMAP_NO_CACHE;
+ global $my_prefs, $IMAP_USE_INTERNAL_DATE;
+
+ $iil_error = "";
+ $iil_errornum = 0;
+
+ //strip slashes
+ // $user = stripslashes($user);
+ // $password = stripslashes($password);
+
+ //set auth method
+ $auth_method = "plain";
+ if (func_num_args() >= 4){
+ $auth_array = func_get_arg(3);
+ if (is_array($auth_array)) $auth_method = $auth_array["imap"];
+ if (empty($auth_method)) $auth_method = "plain";
+ }
+ $message = "INITIAL: $auth_method\n";
+
+ $result = false;
+
+ //initialize connection
+ $conn = new iilConnection;
+ $conn->error="";
+ $conn->errorNum=0;
+ $conn->selected="";
+ $conn->user = $user;
+ $conn->host = $host;
+ $conn->cache = array();
+ $conn->do_cache = (function_exists("cache_write")&&!$IMAP_NO_CACHE);
+ $conn->cache_dirty = array();
+
+ if ($my_prefs['sort_field']=='INTERNALDATE') $IMAP_USE_INTERNAL_DATE = true;
+ else if ($my_prefs['sort_field']=='DATE') $IMAP_USE_INTERNAL_DATE = false;
+ //echo '<!-- conn sort_field: '.$my_prefs['sort_field'].' //-->';
+
+ //check input
+ if (empty($host)) $iil_error .= "Invalid host\n";
+ if (empty($user)) $iil_error .= "Invalid user\n";
+ if (empty($password)) $iil_error .= "Invalid password\n";
+ if (!empty($iil_error)) return false;
+ if (!$ICL_PORT) $ICL_PORT = 143;
+
+ //check for SSL
+ if ($ICL_SSL){
+ $host = "ssl://".$host;
+ }
+
+ //open socket connection
+ $conn->fp = @fsockopen($host, $ICL_PORT, $errno, $errstr, 10);
+ if (!$conn->fp){
+ $iil_error = "Could not connect to $host at port $ICL_PORT: $errstr";
+ $iil_errornum = -1;
+ return false;
+ }
+
+ $iil_error.="Socket connection established\r\n";
+ $line=iil_ReadLine($conn->fp, 300);
+
+ if (strcasecmp($auth_method, "check")==0){
+ //check for supported auth methods
+
+ //default to plain text auth
+ $auth_method = "plain";
+
+ //check for CRAM-MD5
+ fputs($conn->fp, "cp01 CAPABILITY\r\n");
+ do{
+ $line = trim(chop(iil_ReadLine($conn->fp, 100)));
+ $conn->message.="$line\n";
+ $a = explode(" ", $line);
+ if ($line[0]=="*"){
+ while ( list($k, $w) = each($a) ){
+ if ($w!='*' && $w!='CAPABILITY')
+ $conn->capability[] = $w;
+ if ((strcasecmp($w, "AUTH=CRAM_MD5")==0)||
+ (strcasecmp($w, "AUTH=CRAM-MD5")==0)){
+ $auth_method = "auth";
+ }
+ }
+ }
+ }while($a[0]!="cp01");
+ }
+
+ if (strcasecmp($auth_method, "auth")==0){
+ $conn->message.="Trying CRAM-MD5\n";
+ //do CRAM-MD5 authentication
+ fputs($conn->fp, "a000 AUTHENTICATE CRAM-MD5\r\n");
+ $line = trim(chop(iil_ReadLine($conn->fp, 1024)));
+ $conn->message.="$line\n";
+ if ($line[0]=="+"){
+ $conn->message.='Got challenge: '.htmlspecialchars($line)."\n";
+ //got a challenge string, try CRAM-5
+ $result = iil_C_Authenticate($conn, $user, $password, substr($line,2));
+ $conn->message.= "Tried CRAM-MD5: $result \n";
+ }else{
+ $conn->message.='No challenge ('.htmlspecialchars($line)."), try plain\n";
+ $auth = "plain";
+ }
+ }
+
+ if ((!$result)||(strcasecmp($auth, "plain")==0)){
+ //do plain text auth
+ $result = iil_C_Login($conn, $user, $password);
+ $conn->message.="Tried PLAIN: $result \n";
+ }
+
+ $conn->message .= $auth;
+
+ if ($result){
+ iil_C_Namespace($conn);
+ return $conn;
+ }else{
+ $iil_error = $conn->error;
+ $iil_errornum = $conn->errorNum;
+ return false;
+ }
+}
+
+function iil_Close(&$conn){
+ iil_C_WriteCache($conn);
+ if (@fputs($conn->fp, "I LOGOUT\r\n")){
+ fgets($conn->fp, 1024);
+ fclose($conn->fp);
+ $conn->fp = false;
+ }
+}
+
+function iil_ClearCache($user, $host){
+}
+
+
+function iil_C_WriteCache(&$conn){
+ //echo "<!-- doing iil_C_WriteCache //-->\n";
+ if (!$conn->do_cache) return false;
+
+ if (is_array($conn->cache)){
+ while(list($folder,$data)=each($conn->cache)){
+ if ($folder && is_array($data) && $conn->cache_dirty[$folder]){
+ $key = $folder.".imap";
+ $result = cache_write($conn->user, $conn->host, $key, $data, true);
+ //echo "<!-- writing $key $data: $result //-->\n";
+ }
+ }
+ }
+}
+
+function iil_C_EnableCache(&$conn){
+ $conn->do_cache = true;
+}
+
+function iil_C_DisableCache(&$conn){
+ $conn->do_cache = false;
+}
+
+function iil_C_LoadCache(&$conn, $folder){
+ if (!$conn->do_cache) return false;
+
+ $key = $folder.".imap";
+ if (!is_array($conn->cache[$folder])){
+ $conn->cache[$folder] = cache_read($conn->user, $conn->host, $key);
+ $conn->cache_dirty[$folder] = false;
+ }
+}
+
+function iil_C_ExpireCachedItems(&$conn, $folder, $message_set){
+
+ if (!$conn->do_cache) return; //caching disabled
+ if (!is_array($conn->cache[$folder])) return; //cache not initialized|empty
+ if (count($conn->cache[$folder])==0) return; //cache not initialized|empty
+
+ $uids = iil_C_FetchHeaderIndex($conn, $folder, $message_set, "UID");
+ $num_removed = 0;
+ if (is_array($uids)){
+ //echo "<!-- unsetting: ".implode(",",$uids)." //-->\n";
+ while(list($n,$uid)=each($uids)){
+ unset($conn->cache[$folder][$uid]);
+ //$conn->cache[$folder][$uid] = false;
+ //$num_removed++;
+ }
+ $conn->cache_dirty[$folder] = true;
+
+ //echo '<!--'."\n";
+ //print_r($conn->cache);
+ //echo "\n".'//-->'."\n";
+ }else{
+ echo "<!-- failed to get uids: $message_set //-->\n";
+ }
+
+ /*
+ if ($num_removed>0){
+ $new_cache;
+ reset($conn->cache[$folder]);
+ while(list($uid,$item)=each($conn->cache[$folder])){
+ if ($item) $new_cache[$uid] = $conn->cache[$folder][$uid];
+ }
+ $conn->cache[$folder] = $new_cache;
+ }
+ */
+}
+
+function iil_ExplodeQuotedString($delimiter, $string){
+ $quotes=explode("\"", $string);
+ while ( list($key, $val) = each($quotes))
+ if (($key % 2) == 1)
+ $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
+ $string=implode("\"", $quotes);
+
+ $result=explode($delimiter, $string);
+ while ( list($key, $val) = each($result) )
+ $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]);
+
+ return $result;
+}
+
+function iil_CheckForRecent($host, $user, $password, $mailbox){
+ if (empty($mailbox)) $mailbox="INBOX";
+
+ $conn=iil_Connect($host, $user, $password, "plain");
+ $fp = $conn->fp;
+ if ($fp){
+ fputs($fp, "a002 EXAMINE \"$mailbox\"\r\n");
+ do{
+ $line=chop(iil_ReadLine($fp, 300));
+ $a=explode(" ", $line);
+ if (($a[0]=="*") && (strcasecmp($a[2], "RECENT")==0)) $result=(int)$a[1];
+ }while (!iil_StartsWith($a[0],"a002"));
+
+ fputs($fp, "a003 LOGOUT\r\n");
+ fclose($fp);
+ }else $result=-2;
+
+ return $result;
+}
+
+function iil_C_Select(&$conn, $mailbox){
+ $fp = $conn->fp;
+
+ if (empty($mailbox)) return false;
+ if (strcmp($conn->selected, $mailbox)==0) return true;
+
+ iil_C_LoadCache($conn, $mailbox);
+
+ if (fputs($fp, "sel1 SELECT \"$mailbox\"\r\n")){
+ do{
+ $line=chop(iil_ReadLine($fp, 300));
+ $a=explode(" ", $line);
+ if (count($a) == 3){
+ if (strcasecmp($a[2], "EXISTS")==0) $conn->exists=(int)$a[1];
+ if (strcasecmp($a[2], "RECENT")==0) $conn->recent=(int)$a[1];
+ }
+ }while (!iil_StartsWith($line, "sel1"));
+
+ $a=explode(" ", $line);
+
+ if (strcasecmp($a[1],"OK")==0){
+ $conn->selected = $mailbox;
+ return true;
+ }else return false;
+ }else{
+ return false;
+ }
+}
+
+function iil_C_CheckForRecent(&$conn, $mailbox){
+ if (empty($mailbox)) $mailbox="INBOX";
+
+ iil_C_Select($conn, $mailbox);
+ if ($conn->selected==$mailbox) return $conn->recent;
+ else return false;
+}
+
+function iil_C_CountMessages(&$conn, $mailbox, $refresh=false){
+ if ($refresh) $conn->selected="";
+ iil_C_Select($conn, $mailbox);
+ if ($conn->selected==$mailbox) return $conn->exists;
+ else return false;
+}
+
+function iil_SplitHeaderLine($string){
+ $pos=strpos($string, ":");
+ if ($pos>0){
+ $res[0]=substr($string, 0, $pos);
+ $res[1]=trim(substr($string, $pos+1));
+ return $res;
+ }else{
+ return $string;
+ }
+}
+
+function iil_StrToTime($str){
+ global $IMAP_MONTHS,$IMAP_SERVER_TZ;
+
+ if ($str) $time1 = strtotime($str);
+ if ($time1 && $time1!=-1) return $time1-$IMAP_SERVER_TZ;
+
+ //echo '<!--'.$str.'//-->';
+
+ //replace double spaces with single space
+ $str = trim($str);
+ $str = str_replace(" ", " ", $str);
+
+ //strip off day of week
+ $pos=strpos($str, " ");
+ if (!is_numeric(substr($str, 0, $pos))) $str = substr($str, $pos+1);
+
+ //explode, take good parts
+ $a=explode(" ",$str);
+ //$month_a=array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12);
+ $month_str=$a[1];
+ $month=$IMAP_MONTHS[$month_str];
+ $day=(int)$a[0];
+ $year=(int)$a[2];
+ $time=$a[3];
+ $tz_str = $a[4];
+ $tz = substr($tz_str, 0, 3);
+ $ta = explode(":",$time);
+ $hour=(int)$ta[0]-(int)$tz;
+ $minute=(int)$ta[1];
+ $second=(int)$ta[2];
+
+ //make UNIX timestamp
+ $time2 = mktime($hour, $minute, $second, $month, $day, $year);
+ //echo '<!--'.$time1.' '.$time2.' //-->'."\n";
+ return $time2;
+}
+
+function iil_C_Sort(&$conn, $mailbox, $field, $add='', $is_uid=FALSE, $encoding='US-ASCII'){
+ /* Do "SELECT" command */
+ if (!iil_C_Select($conn, $mailbox)) return false;
+
+ $field = strtoupper($field);
+ if ($field=='INTERNALDATE') $field='ARRIVAL';
+ $fields = array('ARRIVAL'=>1,'CC'=>1,'DATE'=>1,'FROM'=>1,'SIZE'=>1,'SUBJECT'=>1,'TO'=>1);
+
+ if (!$fields[$field])
+ return false;
+
+ $is_uid = $is_uid ? 'UID ' : '';
+
+ if (!empty($add))
+ $add = " $add";
+
+ $fp = $conn->fp;
+ $command = 's '. $is_uid .'SORT ('.$field.') '.$encoding.' ALL'."$add\r\n";
+ $line = $data = '';
+
+ if (!fputs($fp, $command)) return false;
+ do{
+ $line = chop(iil_ReadLine($fp, 1024));
+ if (iil_StartsWith($line, '* SORT')) $data.=($data?' ':'').substr($line,7);
+ }while($line[0]!='s');
+
+ if (empty($data)){
+ $conn->error = $line;
+ return false;
+ }
+
+ $out = explode(' ',$data);
+ return $out;
+}
+
+function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field, $normalize=true){
+ global $IMAP_USE_INTERNAL_DATE;
+
+ $c=0;
+ $result=array();
+ $fp = $conn->fp;
+
+ if (empty($index_field)) $index_field="DATE";
+ $index_field = strtoupper($index_field);
+
+ list($from_idx, $to_idx) = explode(':', $message_set);
+ if (empty($message_set) || (isset($to_idx) && (int)$from_idx > (int)$to_idx))
+ return false;
+
+ //$fields_a["DATE"] = ($IMAP_USE_INTERNAL_DATE?6:1);
+ $fields_a['DATE'] = 1;
+ $fields_a['INTERNALDATE'] = 6;
+ $fields_a['FROM'] = 1;
+ $fields_a['REPLY-TO'] = 1;
+ $fields_a['SENDER'] = 1;
+ $fields_a['TO'] = 1;
+ $fields_a['SUBJECT'] = 1;
+ $fields_a['UID'] = 2;
+ $fields_a['SIZE'] = 2;
+ $fields_a['SEEN'] = 3;
+ $fields_a['RECENT'] = 4;
+ $fields_a['DELETED'] = 5;
+
+ $mode=$fields_a[$index_field];
+ if (!($mode > 0)) return false;
+
+ /* Do "SELECT" command */
+ if (!iil_C_Select($conn, $mailbox)) return false;
+
+ /* FETCH date,from,subject headers */
+ if ($mode==1){
+ $key="fhi".($c++);
+ $request=$key." FETCH $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)])\r\n";
+ if (!fputs($fp, $request)) return false;
+ do{
+
+ $line=chop(iil_ReadLine($fp, 200));
+ $a=explode(" ", $line);
+ if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[strlen($line)-1]!=")")){
+ $id=$a[1];
+
+ $str=$line=chop(iil_ReadLine($fp, 300));
+
+ while($line[0]!=")"){ //caution, this line works only in this particular case
+ $line=chop(iil_ReadLine($fp, 300));
+ if ($line[0]!=")"){
+ if (ord($line[0]) <= 32){ //continuation from previous header line
+ $str.=" ".trim($line);
+ }
+ if ((ord($line[0]) > 32) || (strlen($line[0]) == 0)){
+ list($field, $string) = iil_SplitHeaderLine($str);
+ if (strcasecmp($field, "date")==0){
+ $result[$id]=iil_StrToTime($string);
+ }else{
+ $result[$id] = str_replace("\"", "", $string);
+ if ($normalize) $result[$id]=strtoupper($result[$id]);
+ }
+ $str=$line;
+ }
+ }
+ }
+ }
+ /*
+ $end_pos = strlen($line)-1;
+ if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[$end_pos]=="}")){
+ $id = $a[1];
+ $pos = strrpos($line, "{")+1;
+ $bytes = (int)substr($line, $pos, $end_pos-$pos);
+ $received = 0;
+ do{
+ $line = iil_ReadLine($fp, 0);
+ $received+=strlen($line);
+ $line = chop($line);
+
+ if ($received>$bytes) break;
+ else if (!$line) continue;
+
+ list($field,$string)=explode(": ", $line);
+
+ if (strcasecmp($field, "date")==0)
+ $result[$id] = iil_StrToTime($string);
+ else if ($index_field!="DATE")
+ $result[$id]=strtoupper(str_replace("\"", "", $string));
+ }while($line[0]!=")");
+ }else{
+ //one line response, not expected so ignore
+ }
+ */
+ }while(!iil_StartsWith($line, $key));
+ }else if ($mode==6){
+ $key="fhi".($c++);
+ $request = $key." FETCH $message_set (INTERNALDATE)\r\n";
+ if (!fputs($fp, $request)) return false;
+ do{
+ $line=chop(iil_ReadLine($fp, 200));
+ if ($line[0]=="*"){
+ //original: "* 10 FETCH (INTERNALDATE "31-Jul-2002 09:18:02 -0500")"
+ $paren_pos = strpos($line, "(");
+ $foo = substr($line, 0, $paren_pos);
+ $a = explode(" ", $foo);
+ $id = $a[1];
+
+ $open_pos = strpos($line, "\"") + 1;
+ $close_pos = strrpos($line, "\"");
+ if ($open_pos && $close_pos){
+ $len = $close_pos - $open_pos;
+ $time_str = substr($line, $open_pos, $len);
+ $result[$id] = strtotime($time_str);
+ }
+ }else{
+ $a = explode(" ", $line);
+ }
+ }while(!iil_StartsWith($a[0], $key));
+ }else{
+ if ($mode >= 3) $field_name="FLAGS";
+ else if ($index_field=="SIZE") $field_name="RFC822.SIZE";
+ else $field_name=$index_field;
+
+ /* FETCH uid, size, flags */
+ $key="fhi".($c++);
+ $request=$key." FETCH $message_set ($field_name)\r\n";
+
+ if (!fputs($fp, $request)) return false;
+ do{
+ $line=chop(iil_ReadLine($fp, 200));
+ $a = explode(" ", $line);
+ if (($line[0]=="*") && ($a[2]=="FETCH")){
+ $line=str_replace("(", "", $line);
+ $line=str_replace(")", "", $line);
+ $a=explode(" ", $line);
+
+ $id=$a[1];
+
+ if (isset($result[$id])) continue; //if we already got the data, skip forward
+ if ($a[3]!=$field_name) continue; //make sure it's returning what we requested
+
+ /* Caution, bad assumptions, next several lines */
+ if ($mode==2) $result[$id]=$a[4];
+ else{
+ $haystack=strtoupper($line);
+ $result[$id]=(strpos($haystack, $index_field) > 0 ? "F" : "N");
+ }
+ }
+ }while(!iil_StartsWith($line, $key));
+ }
+
+ //check number of elements...
+ list($start_mid,$end_mid)=explode(':',$message_set);
+ if (is_numeric($start_mid) && is_numeric($end_mid)){
+ //count how many we should have
+ $should_have = $end_mid - $start_mid +1;
+
+ //if we have less, try and fill in the "gaps"
+ if (count($result)<$should_have){
+ for($i=$start_mid;$i<=$end_mid;$i++) if (!isset($result[$i])) $result[$i] = '';
+ }
+ }
+
+ return $result;
+
+}
+
+function iil_CompressMessageSet($message_set){
+ //given a comma delimited list of independent mid's,
+ //compresses by grouping sequences together
+
+ //if less than 255 bytes long, let's not bother
+ if (strlen($message_set)<255) return $message_set;
+
+ //see if it's already been compress
+ if (strpos($message_set,':')!==false) return $message_set;
+
+ //separate, then sort
+ $ids = explode(',',$message_set);
+ sort($ids);
+
+ $result = array();
+ $start = $prev = $ids[0];
+ foreach($ids as $id){
+ $incr = $id - $prev;
+ if ($incr>1){ //found a gap
+ if ($start==$prev) $result[] = $prev; //push single id
+ else $result[] = $start.':'.$prev; //push sequence as start_id:end_id
+ $start = $id; //start of new sequence
+ }
+ $prev = $id;
+ }
+ //handle the last sequence/id
+ if ($start==$prev) $result[] = $prev;
+ else $result[] = $start.':'.$prev;
+
+ //return as comma separated string
+ return implode(',',$result);
+}
+
+function iil_C_UIDsToMIDs(&$conn, $mailbox, $uids){
+ if (!is_array($uids) || count($uids)==0) return array();
+ return iil_C_Search($conn, $mailbox, "UID ".implode(",", $uids));
+}
+
+function iil_C_UIDToMID(&$conn, $mailbox, $uid){
+ $result = iil_C_UIDsToMIDs($conn, $mailbox, array($uid));
+ if (count($result)==1) return $result[0];
+ else return false;
+}
+
+function iil_C_FetchUIDs(&$conn,$mailbox){
+ global $clock;
+
+ $num = iil_C_CountMessages($conn, $mailbox);
+ if ($num==0) return array();
+ $message_set = '1'.($num>1?':'.$num:'');
+
+ //if cache not enabled, just call iil_C_FetchHeaderIndex on 'UID' field
+ if (!$conn->do_cache)
+ return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
+
+ //otherwise, let's check cache first
+ $key = $mailbox.'.uids';
+ $cache_good = true;
+ if ($conn->uid_cache) $data = $conn->uid_cache;
+ else $data = cache_read($conn->user, $conn->host, $key);
+
+ //was anything cached at all?
+ if ($data===false) $cache_good = -1;
+
+ //make sure number of messages were the same
+ if ($cache_good>0 && $data['n']!=$num) $cache_good = -2;
+
+ //if everything's okay so far...
+ if ($cache_good>0){
+ //check UIDs of highest mid with current and cached
+ $temp = iil_C_Search($conn, $mailbox, 'UID '.$data['d'][$num]);
+ if (!$temp || !is_array($temp) || $temp[0]!=$num) $cache_good=-3;
+ }
+
+ //if cached data's good, return it
+ if ($cache_good>0){
+ return $data['d'];
+ }
+
+ //otherwise, we need to fetch it
+ $data = array('n'=>$num,'d'=>array());
+ $data['d'] = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
+ cache_write($conn->user, $conn->host, $key, $data);
+ $conn->uid_cache = $data;
+ return $data['d'];
+}
+
+function iil_SortThreadHeaders($headers, $index_a, $uids){
+ asort($index_a);
+ $result = array();
+ foreach($index_a as $mid=>$foobar){
+ $uid = $uids[$mid];
+ $result[$uid] = $headers[$uid];
+ }
+ return $result;
+}
+
+function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set){
+ global $clock;
+ global $index_a;
+
+ list($from_idx, $to_idx) = explode(':', $message_set);
+ if (empty($message_set) || (isset($to_idx) && (int)$from_idx > (int)$to_idx))
+ return false;
+
+ $result = array();
+ $uids = iil_C_FetchUIDs($conn, $mailbox);
+ $debug = false;
+
+ /* Get cached records where possible */
+ if ($conn->do_cache){
+ $cached = cache_read($conn->user, $conn->host, $mailbox.'.thhd');
+ if ($cached && is_array($uids) && count($uids)>0){
+ $needed_set = "";
+ foreach($uids as $id=>$uid){
+ if ($cached[$uid]){
+ $result[$uid] = $cached[$uid];
+ $result[$uid]->id = $id;
+ }else $needed_set.=($needed_set?",":"").$id;
+ }
+ if ($needed_set) $message_set = $needed_set;
+ else $message_set = '';
+ }
+ }
+ $message_set = iil_CompressMessageSet($message_set);
+ if ($debug) echo "Still need: ".$message_set;
+
+ /* if we're missing any, get them */
+ if ($message_set){
+ /* FETCH date,from,subject headers */
+ $key="fh";
+ $fp = $conn->fp;
+ $request=$key." FETCH $message_set (BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])\r\n";
+ $mid_to_id = array();
+ if (!fputs($fp, $request)) return false;
+ do{
+ $line = chop(iil_ReadLine($fp, 1024));
+ if ($debug) echo $line."\n";
+ if (ereg('\{[0-9]+\}$', $line)){
+ $a = explode(" ", $line);
+ $new = array();
+
+ $new_thhd = new iilThreadHeader;
+ $new_thhd->id = $a[1];
+ do{
+ $line=chop(iil_ReadLine($fp, 1024),"\r\n");
+ if (iil_StartsWithI($line,'Message-ID:') || (iil_StartsWithI($line,'In-Reply-To:')) || (iil_StartsWithI($line,'SUBJECT:'))){
+ $pos = strpos($line, ":");
+ $field_name = substr($line, 0, $pos);
+ $field_val = substr($line, $pos+1);
+ $new[strtoupper($field_name)] = trim($field_val);
+ }else if (ereg('^[[:space:]]', $line)){
+ $new[strtoupper($field_name)].= trim($line);
+ }
+ }while($line[0]!=')');
+ $new_thhd->sbj = $new['SUBJECT'];
+ $new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1);
+ $new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1);
+
+ $result[$uids[$new_thhd->id]] = $new_thhd;
+ }
+ }while(!iil_StartsWith($line, "fh"));
+ }
+
+ /* sort headers */
+ if (is_array($index_a)){
+ $result = iil_SortThreadHeaders($result, $index_a, $uids);
+ }
+
+ /* write new set to cache */
+ if ($conn->do_cache){
+ if (count($result)!=count($cached))
+ cache_write($conn->user, $conn->host, $mailbox.'.thhd', $result);
+ }
+
+ //echo 'iil_FetchThreadHeaders:'."\n";
+ //print_r($result);
+
+ return $result;
+}
+
+function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock){
+ global $index_a;
+
+ list($from_idx, $to_idx) = explode(':', $message_set);
+ if (empty($message_set) || (isset($to_idx) && (int)$from_idx > (int)$to_idx))
+ return false;
+
+ $result=array();
+ $roots=array();
+ $root_mids = array();
+ $sub_mids = array();
+ $strays = array();
+ $messages = array();
+ $fp = $conn->fp;
+ $debug = false;
+
+ $sbj_filter_pat = '[a-zA-Z]{2,3}(\[[0-9]*\])?:([[:space:]]*)';
+
+ /* Do "SELECT" command */
+ if (!iil_C_Select($conn, $mailbox)) return false;
+
+ /* FETCH date,from,subject headers */
+ $mid_to_id = array();
+ $messages = array();
+ $headers = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set);
+ if ($clock) $clock->register('fetched headers');
+
+ if ($debug) print_r($headers);
+
+ /* go through header records */
+ foreach($headers as $header){
+ //$id = $header['i'];
+ //$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'],
+ // 'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']);
+ $id = $header->id;
+ $new = array('id'=>$id, 'MESSAGE-ID'=>$header->mid,
+ 'IN-REPLY-TO'=>$header->irt, 'SUBJECT'=>$header->sbj);
+
+ /* add to message-id -> mid lookup table */
+ $mid_to_id[$new['MESSAGE-ID']] = $id;
+
+ /* if no subject, use message-id */
+ if (empty($new['SUBJECT'])) $new['SUBJECT'] = $new['MESSAGE-ID'];
+
+ /* if subject contains 'RE:' or has in-reply-to header, it's a reply */
+ $sbj_pre ='';
+ $has_re = false;
+ if (eregi($sbj_filter_pat, $new['SUBJECT'])) $has_re = true;
+ if ($has_re||$new['IN-REPLY-TO']) $sbj_pre = 'RE:';
+
+ /* strip out 're:', 'fw:' etc */
+ if ($has_re) $sbj = ereg_replace($sbj_filter_pat,'', $new['SUBJECT']);
+ else $sbj = $new['SUBJECT'];
+ $new['SUBJECT'] = $sbj_pre.$sbj;
+
+
+ /* if subject not a known thread-root, add to list */
+ if ($debug) echo $id.' '.$new['SUBJECT']."\t".$new['MESSAGE-ID']."\n";
+ $root_id = $roots[$sbj];
+
+ if ($root_id && ($has_re || !$root_in_root[$root_id])){
+ if ($debug) echo "\tfound root: $root_id\n";
+ $sub_mids[$new['MESSAGE-ID']] = $root_id;
+ $result[$root_id][] = $id;
+ }else if (!isset($roots[$sbj])||(!$has_re&&$root_in_root[$root_id])){
+ /* try to use In-Reply-To header to find root
+ unless subject contains 'Re:' */
+ if ($has_re&&$new['IN-REPLY-TO']){
+ if ($debug) echo "\tlooking: ".$new['IN-REPLY-TO']."\n";
+
+ //reply to known message?
+ $temp = $sub_mids[$new['IN-REPLY-TO']];
+
+ if ($temp){
+ //found it, root:=parent's root
+ if ($debug) echo "\tfound parent: ".$new['SUBJECT']."\n";
+ $result[$temp][] = $id;
+ $sub_mids[$new['MESSAGE-ID']] = $temp;
+ $sbj = '';
+ }else{
+ //if we can't find referenced parent, it's a "stray"
+ $strays[$id] = $new['IN-REPLY-TO'];
+ }
+ }
+
+ //add subject as root
+ if ($sbj){
+ if ($debug) echo "\t added to root\n";
+ $roots[$sbj] = $id;
+ $root_in_root[$id] = !$has_re;
+ $sub_mids[$new['MESSAGE-ID']] = $id;
+ $result[$id] = array($id);
+ }
+ if ($debug) echo $new['MESSAGE-ID']."\t".$sbj."\n";
+ }
+
+ }
+
+ //now that we've gone through all the messages,
+ //go back and try and link up the stray threads
+ if (count($strays)>0){
+ foreach($strays as $id=>$irt){
+ $root_id = $sub_mids[$irt];
+ if (!$root_id || $root_id==$id) continue;
+ $result[$root_id] = array_merge($result[$root_id],$result[$id]);
+ unset($result[$id]);
+ }
+ }
+
+ if ($clock) $clock->register('data prepped');
+
+ if ($debug) print_r($roots);
+ //print_r($result);
+ return $result;
+}
+
+
+function iil_SortThreads(&$tree, $index, $sort_order='ASC'){
+ if (!is_array($tree) || !is_array($index)) return false;
+
+ //create an id to position lookup table
+ $i = 0;
+ foreach($index as $id=>$val){
+ $i++;
+ $index[$id] = $i;
+ }
+ $max = $i+1;
+
+ //for each tree, set array key to position
+ $itree = array();
+ foreach($tree as $id=>$node){
+ if (count($tree[$id])<=1){
+ //for "threads" with only one message, key is position of that message
+ $n = $index[$id];
+ $itree[$n] = array($n=>$id);
+ }else{
+ //for "threads" with multiple messages,
+ $min = $max;
+ $new_a = array();
+ foreach($tree[$id] as $mid){
+ $new_a[$index[$mid]] = $mid; //create new sub-array mapping position to id
+ $pos = $index[$mid];
+ if ($pos&&$pos<$min) $min = $index[$mid]; //find smallest position
+ }
+ $n = $min; //smallest position of child is thread position
+
+ //assign smallest position to root level key
+ //set children array to one created above
+ ksort($new_a);
+ $itree[$n] = $new_a;
+ }
+ }
+
+
+ //sort by key, this basically sorts all threads
+ ksort($itree);
+ $i=0;
+ $out=array();
+ foreach($itree as $k=>$node){
+ $out[$i] = $itree[$k];
+ $i++;
+ }
+
+ //return
+ return $out;
+}
+
+function iil_IndexThreads(&$tree){
+ /* creates array mapping mid to thread id */
+
+ if (!is_array($tree)) return false;
+
+ $t_index = array();
+ foreach($tree as $pos=>$kids){
+ foreach($kids as $kid) $t_index[$kid] = $pos;
+ }
+
+ return $t_index;
+}
+
+function iil_C_FetchHeaders(&$conn, $mailbox, $message_set){
+ global $IMAP_USE_INTERNAL_DATE;
+
+ $c=0;
+ $result=array();
+ $fp = $conn->fp;
+
+ list($from_idx, $to_idx) = explode(':', $message_set);
+ if (empty($message_set) || (isset($to_idx) && (int)$from_idx > (int)$to_idx))
+ return false;
+
+ /* Do "SELECT" command */
+ if (!iil_C_Select($conn, $mailbox)){
+ $conn->error = "Couldn't select $mailbox";
+ return false;
+ }
+
+ /* Get cached records where possible */
+ if ($conn->do_cache){
+ $uids = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, "UID");
+ if (is_array($uids) && count($conn->cache[$mailbox]>0)){
+ $needed_set = "";
+ while(list($id,$uid)=each($uids)){
+ if ($conn->cache[$mailbox][$uid]){
+ $result[$id] = $conn->cache[$mailbox][$uid];
+ $result[$id]->id = $id;
+ }else $needed_set.=($needed_set?",":"").$id;
+ }
+ //echo "<!-- iil_C_FetchHeader\nMessage Set: $message_set\nNeeded Set:$needed_set\n//-->\n";
+ if ($needed_set) $message_set = iil_CompressMessageSet($needed_set);
+ else return $result;
+ }
+ }
+
+ /* FETCH date,from,subject headers */
+ $key="fh".($c++);
+ $request=$key." FETCH $message_set (BODY.PEEK[HEADER.FIELDS (DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC BCC CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID REFERENCE)])\r\n";
+
+ if (!fputs($fp, $request)) return false;
+ do{
+ $line=chop(iil_ReadLine($fp, 200));
+ $a=explode(" ", $line);
+ if (($line[0]=="*") && ($a[2]=="FETCH")){
+ $id=$a[1];
+ $result[$id]=new iilBasicHeader;
+ $result[$id]->id = $id;
+ $result[$id]->subject = "";
+ /*
+ Start parsing headers. The problem is, some header "lines" take up multiple lines.
+ So, we'll read ahead, and if the one we're reading now is a valid header, we'll
+ process the previous line. Otherwise, we'll keep adding the strings until we come
+ to the next valid header line.
+ */
+ $i = 0;
+ $lines = array();
+ do{
+ $line = chop(iil_ReadLine($fp, 300),"\r\n");
+ if (ord($line[0])<=32) $lines[$i].=(empty($lines[$i])?"":"\n").trim(chop($line));
+ else{
+ $i++;
+ $lines[$i] = trim(chop($line));
+ }
+ }while($line[0]!=")" && strncmp($line, $key, strlen($key))); // patch from "Maksim Rubis" <siburny@hotmail.com>
+
+ if(strncmp($line, $key, strlen($key)))
+ {
+ //process header, fill iilBasicHeader obj.
+ // initialize
+ if (is_array($headers)){
+ reset($headers);
+ while ( list($k, $bar) = each($headers) ) $headers[$k] = "";
+ }
+
+ // create array with header field:data
+ $headers = array();
+ while ( list($lines_key, $str) = each($lines) ){
+ list($field, $string) = iil_SplitHeaderLine($str);
+ $field = strtolower($field);
+ $headers[$field] = $string;
+ }
+ $result[$id]->date = $headers["date"];
+ $result[$id]->timestamp = iil_StrToTime($headers["date"]);
+ $result[$id]->from = $headers["from"];
+ $result[$id]->to = str_replace("\n", " ", $headers["to"]);
+ $result[$id]->subject = str_replace("\n", "", $headers["subject"]);
+ $result[$id]->replyto = str_replace("\n", " ", $headers["reply-to"]);
+ $result[$id]->cc = str_replace("\n", " ", $headers["cc"]);
+ $result[$id]->bcc = str_replace("\n", " ", $headers["bcc"]);
+ $result[$id]->encoding = str_replace("\n", " ", $headers["content-transfer-encoding"]);
+ $result[$id]->ctype = str_replace("\n", " ", $headers["content-type"]);
+ $result[$id]->in_reply_to = ereg_replace("[\n<>]",'', $headers['in-reply-to']);
+ $result[$id]->reference = $headers["reference"];
+
+ list($result[$id]->ctype, $ctype_add) = explode(";", $headers["content-type"]);
+
+ if (preg_match('/charset="?([a-z0-9\-]+)"?/i', $ctype_add, $regs))
+ $result[$id]->charset = $regs[1];
+
+ $messageID = $headers["message-id"];
+ if (!$messageID) "mid:".$id;
+ $result[$id]->messageID = $messageID;
+ }
+ else {
+ $a=explode(" ", $line);
+ }
+
+ }
+ }while(strcmp($a[0], $key)!=0);
+
+ /*
+ FETCH uid, size, flags
+ Sample reply line: "* 3 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen \Deleted))"
+ */
+ $command_key="fh".($c++);
+ $request= $command_key." FETCH $message_set (UID RFC822.SIZE FLAGS INTERNALDATE)\r\n";
+ if (!fputs($fp, $request)) return false;
+ do{
+ $line=chop(iil_ReadLine($fp, 200));
+ //$a = explode(" ", $line);
+ //if (($line[0]=="*") && ($a[2]=="FETCH")){
+ if ($line[0]=="*"){
+ //echo "<!-- $line //-->\n";
+ //get outter most parens
+ $open_pos = strpos($line, "(") + 1;
+ $close_pos = strrpos($line, ")");
+ if ($open_pos && $close_pos){
+ //extract ID from pre-paren
+ $pre_str = substr($line, 0, $open_pos);
+ $pre_a = explode(" ", $line);
+ $id = $pre_a[1];
+
+ //get data
+ $len = $close_pos - $open_pos;
+ $str = substr($line, $open_pos, $len);
+
+ //swap parents with quotes, then explode
+ $str = eregi_replace("[()]", "\"", $str);
+ $a = iil_ExplodeQuotedString(" ", $str);
+
+ //did we get the right number of replies?
+ $parts_count = count($a);
+ if ($parts_count>=8){
+ for ($i=0;$i<$parts_count;$i=$i+2){
+ if (strcasecmp($a[$i],"UID")==0) $result[$id]->uid=$a[$i+1];
+ else if (strcasecmp($a[$i],"RFC822.SIZE")==0) $result[$id]->size=$a[$i+1];
+ else if (strcasecmp($a[$i],"INTERNALDATE")==0) $time_str = $a[$i+1];
+ else if (strcasecmp($a[$i],"FLAGS")==0) $flags_str = $a[$i+1];
+ }
+
+ // process flags
+ $flags_str = eregi_replace('[\\\"]', "", $flags_str);
+ $flags_a = explode(" ", $flags_str);
+ //echo "<!-- ID: $id FLAGS: ".implode(",", $flags_a)." //-->\n";
+
+ $result[$id]->seen = false;
+ $result[$id]->recent = false;
+ $result[$id]->deleted = false;
+ $result[$id]->answered = false;
+ if (is_array($flags_a)){
+ reset($flags_a);
+ while (list($key,$val)=each($flags_a)){
+ if (strcasecmp($val,"Seen")==0) $result[$id]->seen = true;
+ else if (strcasecmp($val, "Deleted")==0) $result[$id]->deleted=true;
+ else if (strcasecmp($val, "Recent")==0) $result[$id]->recent = true;
+ else if (strcasecmp($val, "Answered")==0) $result[$id]->answered = true;
+ }
+ $result[$id]->flags=$flags_str;
+ }
+
+ // if time is gmt...
+ $time_str = str_replace('GMT','+0000',$time_str);
+
+ //get timezone
+ $time_str = substr($time_str, 0, -1);
+ $time_zone_str = substr($time_str, -5); //extract timezone
+ $time_str = substr($time_str, 1, -6); //remove quotes
+ $time_zone = (float)substr($time_zone_str, 1, 2); //get first two digits
+ if ($time_zone_str[3]!='0') $time_zone += 0.5; //handle half hour offset
+ if ($time_zone_str[0]=="-") $time_zone = $time_zone * -1.0; //minus?
+ $result[$id]->internaldate = $time_str;
+
+ if ($IMAP_USE_INTERNAL_DATE){
+ //calculate timestamp
+ $timestamp = strtotime($time_str); //return's server's time
+ $na_timestamp = $timestamp;
+ $timestamp -= $time_zone * 3600; //compensate for tz, get GMT
+ $result[$id]->timestamp = $timestamp;
+ }
+
+ if ($conn->do_cache){
+ $uid = $result[$id]->uid;
+ $conn->cache[$mailbox][$uid] = $result[$id];
+ $conn->cache_dirty[$mailbox] = true;
+ }
+ //echo "<!-- ID: $id : $time_str -- local: $na_timestamp (".date("F j, Y, g:i a", $na_timestamp).") tz: $time_zone -- GMT: ".$timestamp." (".date("F j, Y, g:i a", $timestamp).") //-->\n";
+ }else{
+ //echo "<!-- ERROR: $id : $str //-->\n";
+ }
+ }
+ }
+ }while(strpos($line, $command_key)===false);
+
+ return $result;
+}
+
+
+function iil_C_FetchHeader(&$conn, $mailbox, $id){
+ $fp = $conn->fp;
+ $a=iil_C_FetchHeaders($conn, $mailbox, $id);
+ if (is_array($a)) return $a[$id];
+ else return false;
+}
+
+
+function iil_SortHeaders($a, $field, $flag){
+ if (empty($field)) $field="uid";
+ $field=strtolower($field);
+ if ($field=="date"||$field=='internaldate') $field="timestamp";
+ if (empty($flag)) $flag="ASC";
+ $flag=strtoupper($flag);
+ $stripArr = ($field=='subject') ? array('Re: ','Fwd: ','Fw: ',"\"") : array("\"");
+
+ $c=count($a);
+ if ($c>0){
+ /*
+ Strategy:
+ First, we'll create an "index" array.
+ Then, we'll use sort() on that array,
+ and use that to sort the main array.
+ */
+
+ // create "index" array
+ $index=array();
+ reset($a);
+ while (list($key, $val)=each($a)){
+
+ if ($field=="timestamp"){
+ $data = @strtotime($val->date);
+ if ($data == false)
+ $data = $val->timestamp;
+ }
+ else {
+ $data = $val->$field;
+ if (is_string($data))
+ $data=strtoupper(str_replace($stripArr, "", $data));
+ }
+
+ $index[$key]=$data;
+ }
+
+ // sort index
+ $i=0;
+ if ($flag=="ASC") asort($index);
+ else arsort($index);
+
+ // form new array based on index
+ $result=array();
+ reset($index);
+ while (list($key, $val)=each($index)){
+ $result[$key]=$a[$key];
+ $i++;
+ }
+ }
+
+ return $result;
+}
+
+function iil_C_Expunge(&$conn, $mailbox){
+ $fp = $conn->fp;
+ if (iil_C_Select($conn, $mailbox)){
+ $c=0;
+ fputs($fp, "exp1 EXPUNGE\r\n");
+ do{
+ $line=chop(iil_ReadLine($fp, 100));
+ if ($line[0]=="*") $c++;
+ }while (!iil_StartsWith($line, "exp1"));
+
+ if (iil_ParseResult($line) == 0){
+ $conn->selected = ""; //state has changed, need to reselect
+ //$conn->exists-=$c;
+ return $c;
+ }else{
+ $conn->error = $line;
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+function iil_C_ModFlag(&$conn, $mailbox, $messages, $flag, $mod){
+ if ($mod!="+" && $mod!="-") return -1;
+
+ $fp = $conn->fp;
+ $flags=array(
+ "SEEN"=>"\\Seen",
+ "DELETED"=>"\\Deleted",
+ "RECENT"=>"\\Recent",
+ "ANSWERED"=>"\\Answered",
+ "DRAFT"=>"\\Draft",
+ "FLAGGED"=>"\\Flagged"
+ );
+ $flag=strtoupper($flag);
+ $flag=$flags[$flag];
+ if (iil_C_Select($conn, $mailbox)){
+ $c=0;
+ fputs($fp, "flg STORE $messages ".$mod."FLAGS (".$flag.")\r\n");
+ do{
+ $line=chop(iil_ReadLine($fp, 100));
+ if ($line[0]=="*") $c++;
+ }while (!iil_StartsWith($line, "flg"));
+
+ if (iil_ParseResult($line) == 0){
+ iil_C_ExpireCachedItems($conn, $mailbox, $messages);
+ return $c;
+ }else{
+ $conn->error = $line;
+ return -1;
+ }
+ }else{
+ $conn->error = "Select failed";
+ return -1;
+ }
+}
+
+function iil_C_Flag(&$conn, $mailbox, $messages, $flag){
+ return iil_C_ModFlag($conn, $mailbox, $messages, $flag, "+");
+}
+
+function iil_C_Unflag(&$conn, $mailbox, $messages, $flag){
+ return iil_C_ModFlag($conn, $mailbox, $messages, $flag, "-");
+}
+
+function iil_C_Delete(&$conn, $mailbox, $messages){
+ return iil_C_ModFlag($conn, $mailbox, $messages, "DELETED", "+");
+}
+
+function iil_C_Undelete(&$conn, $mailbox, $messages){
+ return iil_C_ModFlag($conn, $mailbox, $messages, "DELETED", "-");
+}
+
+
+function iil_C_Unseen(&$conn, $mailbox, $messages){
+ return iil_C_ModFlag($conn, $mailbox, $messages, "SEEN", "-");
+}
+
+
+function iil_C_Copy(&$conn, $messages, $from, $to){
+ $fp = $conn->fp;
+
+ if (empty($from) || empty($to)) return -1;
+
+ if (iil_C_Select($conn, $from)){
+ $c=0;
+
+ fputs($fp, "cpy1 COPY $messages \"$to\"\r\n");
+ $line=iil_ReadReply($fp);
+ return iil_ParseResult($line);
+ }else{
+ return -1;
+ }
+}
+
+function iil_FormatSearchDate($month, $day, $year){
+ $month = (int)$month;
+ $months=array(
+ 1=>"Jan", 2=>"Feb", 3=>"Mar", 4=>"Apr",
+ 5=>"May", 6=>"Jun", 7=>"Jul", 8=>"Aug",
+ 9=>"Sep", 10=>"Oct", 11=>"Nov", 12=>"Dec"
+ );
+ return $day."-".$months[$month]."-".$year;
+}
+
+function iil_C_CountUnseen(&$conn, $folder){
+ $index = iil_C_Search($conn, $folder, "ALL UNSEEN");
+ if (is_array($index)){
+ $str = implode(",", $index);
+ if (empty($str)) return false;
+ else return count($index);
+ }else return false;
+}
+
+function iil_C_UID2ID(&$conn, $folder, $uid){
+ if ($uid > 0){
+ $id_a = iil_C_Search($conn, $folder, "UID $uid");
+ if (is_array($id_a)){
+ $count = count($id_a);
+ if ($count > 1) return false;
+ else return $id_a[0];
+ }
+ }
+ return false;
+}
+
+function iil_C_ID2UID(&$conn, $folder, $id){
+ $fp = $conn->fp;
+ $result=-1;
+ if ($id > 0) {
+ if (iil_C_Select($conn, $folder)){
+ $key = "FUID";
+ if (fputs($fp, "$key FETCH $id (UID)\r\n")){
+ do{
+ $line=chop(iil_ReadLine($fp, 1024));
+ if (eregi("^\* $id FETCH \(UID (.*)\)", $line, $r)){
+ $result = $r[1];
+ }
+ } while (!preg_match("/^$key/", $line));
+ }
+ }
+ }
+ return $result;
+}
+
+function iil_C_Search(&$conn, $folder, $criteria){
+ $fp = $conn->fp;
+ if (iil_C_Select($conn, $folder)){
+ $c=0;
+
+ $query = "srch1 SEARCH ".chop($criteria)."\r\n";
+ fputs($fp, $query);
+ do{
+ $line=trim(chop(iil_ReadLine($fp, 10000)));
+ if (eregi("^\* SEARCH", $line)){
+ $str = trim(substr($line, 8));
+ $messages = explode(" ", $str);
+ }
+ }while(!iil_StartsWith($line, "srch1"));
+
+ $result_code=iil_ParseResult($line);
+ if ($result_code==0) return $messages;
+ else{
+ $conn->error = "iil_C_Search: ".$line."\n";
+ return false;
+ }
+
+ }else{
+ $conn->error = "iil_C_Search: Couldn't select \"$folder\"\n";
+ return false;
+ }
+}
+
+function iil_C_Move(&$conn, $messages, $from, $to){
+ $fp = $conn->fp;
+
+ if (!$from || !$to) return -1;
+
+ $r=iil_C_Copy($conn, $messages, $from,$to);
+ if ($r==0){
+ return iil_C_Delete($conn, $from, $messages);
+ }else{
+ return $r;
+ }
+}
+
+function iil_C_GetHierarchyDelimiter(&$conn){
+ if ($conn->delimiter) return $conn->delimiter;
+
+ $fp = $conn->fp;
+ $delimiter = false;
+
+ //try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8)
+ if (!fputs($fp, "ghd LIST \"\" \"\"\r\n")) return false;
+ do{
+ $line=iil_ReadLine($fp, 500);
+ if ($line[0]=="*"){
+ $line = rtrim($line);
+ $a=iil_ExplodeQuotedString(" ", $line);
+ if ($a[0]=="*") $delimiter = str_replace("\"", "", $a[count($a)-2]);
+ }
+ }while (!iil_StartsWith($line, "ghd"));
+
+ if (strlen($delimiter)>0) return $delimiter;
+
+ //if that fails, try namespace extension
+ //try to fetch namespace data
+ fputs($conn->fp, "ns1 NAMESPACE\r\n");
+ do{
+ $line = iil_ReadLine($conn->fp, 1024);
+ if (iil_StartsWith($line, "* NAMESPACE")){
+ $i = 0;
+ $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
+ }
+ }while(!iil_StartsWith($line, "ns1"));
+
+ if (!is_array($data)) return false;
+
+ //extract user space data (opposed to global/shared space)
+ $user_space_data = $data[0];
+ if (!is_array($user_space_data)) return false;
+
+ //get first element
+ $first_userspace = $user_space_data[0];
+ if (!is_array($first_userspace)) return false;
+
+ //extract delimiter
+ $delimiter = $first_userspace[1];
+
+ return $delimiter;
+}
+
+function iil_C_ListMailboxes(&$conn, $ref, $mailbox){
+ global $IGNORE_FOLDERS;
+
+ $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
+
+ $fp = $conn->fp;
+ if (empty($mailbox)) $mailbox="*";
+ if (empty($ref) && $conn->rootdir) $ref = $conn->rootdir;
+
+ // send command
+ if (!fputs($fp, "lmb LIST \"".$ref."\" \"$mailbox\"\r\n")) return false;
+ $i=0;
+ // get folder list
+ do{
+ $line=iil_ReadLine($fp, 500);
+ $line=iil_MultLine($fp, $line);
+
+ $a = explode(" ", $line);
+ if (($line[0]=="*") && ($a[1]=="LIST")){
+ $line = rtrim($line);
+ // split one line
+ $a=iil_ExplodeQuotedString(" ", $line);
+ // last string is folder name
+ $folder = str_replace("\"", "", $a[count($a)-1]);
+ if (empty($ignore) || (!empty($ignore) && !eregi($ignore, $folder))) $folders[$i] = $folder;
+ // second from last is delimiter
+ $delim = str_replace("\"", "", $a[count($a)-2]);
+ // is it a container?
+ $i++;
+ }
+ }while (!iil_StartsWith($line, "lmb"));
+
+ if (is_array($folders)){
+ if (!empty($ref)){
+ // if rootdir was specified, make sure it's the first element
+ // some IMAP servers (i.e. Courier) won't return it
+ if ($ref[strlen($ref)-1]==$delim) $ref = substr($ref, 0, strlen($ref)-1);
+ if ($folders[0]!=$ref) array_unshift($folders, $ref);
+ }
+ return $folders;
+ }else if (iil_ParseResult($line)==0){
+ return array('INBOX');
+ }else{
+ $conn->error = $line;
+ return false;
+ }
+}
+
+
+function iil_C_ListSubscribed(&$conn, $ref, $mailbox){
+ global $IGNORE_FOLDERS;
+
+ $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
+
+ $fp = $conn->fp;
+ if (empty($mailbox)) $mailbox = "*";
+ if (empty($ref) && $conn->rootdir) $ref = $conn->rootdir;
+ $folders = array();
+
+ // send command
+ if (!fputs($fp, "lsb LSUB \"".$ref."\" \"".$mailbox."\"\r\n")){
+ $conn->error = "Couldn't send LSUB command\n";
+ return false;
+ }
+ $i=0;
+ // get folder list
+ do{
+ $line=iil_ReadLine($fp, 500);
+ $line=iil_MultLine($fp, $line);
+ $a = explode(" ", $line);
+ if (($line[0]=="*") && ($a[1]=="LSUB" || $a[1]=="LIST")){
+ $line = rtrim($line);
+ // split one line
+ $a=iil_ExplodeQuotedString(" ", $line);
+ // last string is folder name
+ //$folder = UTF7DecodeString(str_replace("\"", "", $a[count($a)-1]));
+ $folder = str_replace("\"", "", $a[count($a)-1]);
+ if ((!in_array($folder, $folders)) && (empty($ignore) || (!empty($ignore) && !eregi($ignore, $folder)))) $folders[$i] = $folder;
+ // second from last is delimiter
+ $delim = str_replace("\"", "", $a[count($a)-2]);
+ // is it a container?
+ $i++;
+ }
+ }while (!iil_StartsWith($line, "lsb"));
+
+ if (is_array($folders)){
+ if (!empty($ref)){
+ // if rootdir was specified, make sure it's the first element
+ // some IMAP servers (i.e. Courier) won't return it
+ if ($ref[strlen($ref)-1]==$delim) $ref = substr($ref, 0, strlen($ref)-1);
+ if ($folders[0]!=$ref) array_unshift($folders, $ref);
+ }
+ return $folders;
+ }else{
+ $conn->error = $line;
+ return false;
+ }
+}
+
+
+function iil_C_Subscribe(&$conn, $folder){
+ $fp = $conn->fp;
+
+ $query = "sub1 SUBSCRIBE \"".$folder."\"\r\n";
+ fputs($fp, $query);
+ $line=trim(chop(iil_ReadLine($fp, 10000)));
+ return iil_ParseResult($line);
+}
+
+
+function iil_C_UnSubscribe(&$conn, $folder){
+ $fp = $conn->fp;
+
+ $query = "usub1 UNSUBSCRIBE \"".$folder."\"\r\n";
+ fputs($fp, $query);
+ $line=trim(chop(iil_ReadLine($fp, 10000)));
+ return iil_ParseResult($line);
+}
+
+
+function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part){
+ $fp = $conn->fp;
+ $result=false;
+ if (($part==0)||(empty($part))) $part="HEADER";
+ else $part.=".MIME";
+
+ if (iil_C_Select($conn, $mailbox)){
+ $key="fh".($c++);
+ $request=$key." FETCH $id (BODY.PEEK[$part])\r\n";
+ if (!fputs($fp, $request)) return false;
+ do{
+ $line=chop(iil_ReadLine($fp, 200));
+ $a=explode(" ", $line);
+ if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[strlen($line)-1]!=")")){
+ $line=iil_ReadLine($fp, 300);
+ while(chop($line)!=")"){
+ $result.=$line;
+ $line=iil_ReadLine($fp, 300);
+ }
+ }
+ }while(strcmp($a[0], $key)!=0);
+ }
+
+ return $result;
+}
+
+
+function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part, $mode){
+ /* modes:
+ 1: return string
+ 2: print
+ 3: base64 and print
+ */
+ $fp = $conn->fp;
+ $result=false;
+ if (($part==0)||(empty($part))) $part="TEXT";
+
+ if (iil_C_Select($conn, $mailbox)){
+ $reply_key="* ".$id;
+ // format request
+ $key="ftch".($c++)." ";
+ $request=$key."FETCH $id (BODY.PEEK[$part])\r\n";
+ // send request
+ if (!fputs($fp, $request)) return false;
+ // receive reply line
+ do{
+ $line = chop(iil_ReadLine($fp, 1000));
+ $a = explode(" ", $line);
+ }while ($a[2]!="FETCH");
+ $len = strlen($line);
+ if ($line[$len-1] == ")"){
+ //one line response, get everything between first and last quotes
+ $from = strpos($line, "\"") + 1;
+ $to = strrpos($line, "\"");
+ $len = $to - $from;
+ if ($mode==1) $result = substr($line, $from, $len);
+ else if ($mode==2) echo substr($line, $from, $len);
+ else if ($mode==3) echo base64_decode(substr($line, $from, $len));
+ }else if ($line[$len-1] == "}"){
+ //multi-line request, find sizes of content and receive that many bytes
+ $from = strpos($line, "{") + 1;
+ $to = strrpos($line, "}");
+ $len = $to - $from;
+ $sizeStr = substr($line, $from, $len);
+ $bytes = (int)$sizeStr;
+ $received = 0;
+ while ($received < $bytes){
+ $remaining = $bytes - $received;
+ $line = iil_ReadLine($fp, 1024);
+ $len = strlen($line);
+ if ($len > $remaining) substr($line, 0, $remaining);
+ $received += strlen($line);
+ if ($mode==1) $result .= chop($line)."\n";
+ else if ($mode==2){ echo chop($line)."\n"; flush(); }
+ else if ($mode==3){ echo base64_decode($line); flush(); }
+ }
+ }
+ // read in anything up until 'til last line
+ do{
+ $line = iil_ReadLine($fp, 1024);
+ }while(!iil_StartsWith($line, $key));
+
+ if ($result){
+ $result = chop($result);
+ return $result; // substr($result, 0, strlen($result)-1);
+ }else return false;
+ }else{
+ echo "Select failed.";
+ }
+
+ if ($mode==1) return $result;
+ else return $received;
+}
+
+function iil_C_FetchPartBody(&$conn, $mailbox, $id, $part){
+ return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1);
+}
+
+function iil_C_PrintPartBody(&$conn, $mailbox, $id, $part){
+ iil_C_HandlePartBody($conn, $mailbox, $id, $part, 2);
+}
+
+function iil_C_PrintBase64Body(&$conn, $mailbox, $id, $part){
+ iil_C_HandlePartBody($conn, $mailbox, $id, $part, 3);
+}
+
+function iil_C_CreateFolder(&$conn, $folder){
+ $fp = $conn->fp;
+ if (fputs($fp, "c CREATE \"".$folder."\"\r\n")){
+ do{
+ $line=iil_ReadLine($fp, 300);
+ }while($line[0]!="c");
+ $conn->error = $line;
+ return (iil_ParseResult($line)==0);
+ }else{
+ return false;
+ }
+}
+
+function iil_C_RenameFolder(&$conn, $from, $to){
+ $fp = $conn->fp;
+ if (fputs($fp, "r RENAME \"".$from."\" \"".$to."\"\r\n")){
+ do{
+ $line=iil_ReadLine($fp, 300);
+ }while($line[0]!="r");
+ return (iil_ParseResult($line)==0);
+ }else{
+ return false;
+ }
+}
+
+function iil_C_DeleteFolder(&$conn, $folder){
+ $fp = $conn->fp;
+ if (fputs($fp, "d DELETE \"".$folder."\"\r\n")){
+ do{
+ $line=iil_ReadLine($fp, 300);
+ }while($line[0]!="d");
+ return (iil_ParseResult($line)==0);
+ }else{
+ $conn->error = "Couldn't send command\n";
+ return false;
+ }
+}
+
+function iil_C_Append(&$conn, $folder, &$message){
+ if (!$folder) return false;
+ $fp = $conn->fp;
+
+ $message = str_replace("\r", "", $message);
+ $message = str_replace("\n", "\r\n", $message);
+
+ $len = strlen($message);
+ if (!$len) return false;
+
+ $request="A APPEND \"".$folder."\" (\\Seen) {".$len."}\r\n";
+ if (fputs($fp, $request)){
+ $line=iil_ReadLine($fp, 100);
+ $sent = fwrite($fp, $message."\r\n");
+ flush();
+ do{
+ $line=iil_ReadLine($fp, 1000);
+ }while($line[0]!="A");
+
+ $result = (iil_ParseResult($line)==0);
+ if (!$result) $conn->error .= $line."\n";
+ return $result;
+
+ }else{
+ $conn->error .= "Couldn't send command \"$request\"\n";
+ return false;
+ }
+}
+
+
+function iil_C_AppendFromFile(&$conn, $folder, $path){
+ if (!$folder) return false;
+
+ //open message file
+ $in_fp = false;
+ if (file_exists(realpath($path))) $in_fp = fopen($path, "r");
+ if (!$in_fp){
+ $conn->error .= "Couldn't open $path for reading\n";
+ return false;
+ }
+
+ $fp = $conn->fp;
+ $len = filesize($path);
+ if (!$len) return false;
+
+ //send APPEND command
+ $request="A APPEND \"".$folder."\" (\\Seen) {".$len."}\r\n";
+ $bytes_sent = 0;
+ if (fputs($fp, $request)){
+ $line=iil_ReadLine($fp, 100);
+
+ //send file
+ while(!feof($in_fp)){
+ $buffer = fgets($in_fp, 4096);
+ $bytes_sent += strlen($buffer);
+ fputs($fp, $buffer);
+ }
+ fclose($in_fp);
+
+ fputs($fp, "\r\n");
+
+ //read response
+ do{
+ $line=iil_ReadLine($fp, 1000);
+ }while($line[0]!="A");
+
+ $result = (iil_ParseResult($line)==0);
+ if (!$result) $conn->error .= $line."\n";
+ return $result;
+
+ }else{
+ $conn->error .= "Couldn't send command \"$request\"\n";
+ return false;
+ }
+}
+
+
+function iil_C_FetchStructureString(&$conn, $folder, $id){
+ $fp = $conn->fp;
+ $result=false;
+ if (iil_C_Select($conn, $folder)){
+ $key = "F1247";
+ if (fputs($fp, "$key FETCH $id (BODYSTRUCTURE)\r\n")){
+ do{
+ $line=chop(iil_ReadLine($fp, 5000));
+ if ($line[0]=="*"){
+ if (ereg("\}$", $line)){
+ preg_match('/(.+)\{([0-9]+)\}/', $line, $match);
+ $result = $match[1];
+ do{
+ $line = chop(iil_ReadLine($fp, 100));
+ if (!preg_match("/^$key/", $line)) $result .= $line;
+ else $done = true;
+ }while(!$done);
+ }else{
+ $result = $line;
+ }
+ list($pre, $post) = explode("BODYSTRUCTURE ", $result);
+ $result = substr($post, 0, strlen($post)-1); //truncate last ')' and return
+ }
+ }while (!preg_match("/^$key/",$line));
+ }
+ }
+ return $result;
+}
+
+function iil_C_PrintSource(&$conn, $folder, $id, $part){
+ $header = iil_C_FetchPartHeader($conn, $folder, $id, $part);
+ //echo str_replace("\r", "", $header);
+ echo $header;
+ echo iil_C_PrintPartBody($conn, $folder, $id, $part);
+}
+
+function iil_C_GetQuota(&$conn){
+/*
+b GETQUOTAROOT "INBOX"
+* QUOTAROOT INBOX user/rchijiiwa1
+* QUOTA user/rchijiiwa1 (STORAGE 654 9765)
+b OK Completed
+*/
+ $fp = $conn->fp;
+ $result=false;
+ $quota_line = "";
+
+ //get line containing quota info
+ if (fputs($fp, "QUOT1 GETQUOTAROOT \"INBOX\"\r\n")){
+ do{
+ $line=chop(iil_ReadLine($fp, 5000));
+ if (iil_StartsWith($line, "* QUOTA ")) $quota_line = $line;
+ }while(!iil_StartsWith($line, "QUOT1"));
+ }
+
+ //return false if not found, parse if found
+ if (!empty($quota_line)){
+ $quota_line = eregi_replace("[()]", "", $quota_line);
+ $parts = explode(" ", $quota_line);
+ $storage_part = array_search("STORAGE", $parts);
+ if ($storage_part>0){
+ $result = array();
+ $used = $parts[$storage_part+1];
+ $total = $parts[$storage_part+2];
+ $result["used"] = $used;
+ $result["total"] = (empty($total)?"??":$total);
+ $result["percent"] = (empty($total)?"??":round(($used/$total)*100));
+ $result["free"] = 100 - $result["percent"];
+ }
+ }
+
+ return $result;
+}
+
+
+function iil_C_ClearFolder(&$conn, $folder){
+ $num_in_trash = iil_C_CountMessages($conn, $folder);
+ if ($num_in_trash > 0) iil_C_Delete($conn, $folder, "1:".$num_in_trash);
+ return (iil_C_Expunge($conn, $folder) >= 0);
+}
+
+?>
--- /dev/null
+<?php
+/////////////////////////////////////////////////////////
+//
+// Iloha MIME Library (IML)
+//
+// (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org>
+//
+// This file is part of IlohaMail. IlohaMail is free software released
+// under the GPL license. See enclosed file COPYING for details, or
+// see http://www.fsf.org/copyleft/gpl.html
+//
+/////////////////////////////////////////////////////////
+
+/********************************************************
+
+ FILE: include/mime.inc
+ PURPOSE:
+ Provide functions for handling mime messages.
+ USAGE:
+ Use iil_C_FetchStructureString to get IMAP structure stirng, then pass that through
+ iml_GetRawStructureArray() to get root node to a nested data structure.
+ Pass root node to the iml_GetPart*() functions to retreive individual bits of info.
+
+********************************************************/
+$MIME_INVALID = -1;
+$MIME_TEXT = 0;
+$MIME_MULTIPART = 1;
+$MIME_MESSAGE = 2;
+$MIME_APPLICATION = 3;
+$MIME_AUDIO = 4;
+$MIME_IMAGE = 5;
+$MIME_VIDEO = 6;
+$MIME_OTHER = 7;
+
+function iml_ClosingParenPos($str, $start){
+ $level=0;
+ $len = strlen($str);
+ $in_quote = 0;
+ for ($i=$start;$i<$len;$i++){
+ if ($str[$i]=="\"") $in_quote = ($in_quote + 1) % 2;
+ if (!$in_quote){
+ if ($str[$i]=="(") $level++;
+ else if (($level > 0) && ($str[$i]==")")) $level--;
+ else if (($level == 0) && ($str[$i]==")")) return $i;
+ }
+ }
+}
+
+function iml_ParseBSString($str){
+
+ $id = 0;
+ $a = array();
+ $len = strlen($str);
+
+ $in_quote = 0;
+ for ($i=0; $i<$len; $i++){
+ if ($str[$i] == "\"") $in_quote = ($in_quote + 1) % 2;
+ else if (!$in_quote){
+ if ($str[$i] == " ") $id++; //space means new element
+ else if ($str[$i]=="("){ //new part
+ $i++;
+ $endPos = iml_ClosingParenPos($str, $i);
+ $partLen = $endPos - $i;
+ $part = substr($str, $i, $partLen);
+ $a[$id] = iml_ParseBSString($part); //send part string
+ if ($verbose){
+ echo "{>".$endPos."}";
+ flush();
+ }
+ $i = $endPos;
+ }else $a[$id].=$str[$i]; //add to current element in array
+ }else if ($in_quote){
+ if ($str[$i]=="\\") $i++; //escape backslashes
+ else $a[$id].=$str[$i]; //add to current element in array
+ }
+ }
+
+ reset($a);
+ return $a;
+}
+
+function iml_GetRawStructureArray($str){
+ $line=substr($str, 1, strlen($str) - 2);
+ $line = str_replace(")(", ") (", $line);
+
+ $struct = iml_ParseBSString($line);
+ if ((strcasecmp($struct[0], "message")==0) && (strcasecmp($struct[1], "rfc822")==0)){
+ $struct = array($struct);
+ }
+ return $struct;
+}
+
+function iml_GetPartArray($a, $part){
+ if (!is_array($a)) return false;
+ if (strpos($part, ".") > 0){
+ $original_part = $part;
+ $pos = strpos($part, ".");
+ $rest = substr($original_part, $pos+1);
+ $part = substr($original_part, 0, $pos);
+ if ((strcasecmp($a[0], "message")==0) && (strcasecmp($a[1], "rfc822")==0)){
+ $a = $a[8];
+ }
+ //echo "m - part: $original_part current: $part rest: $rest array: ".implode(" ", $a)."<br>\n";
+ return iml_GetPartArray($a[$part-1], $rest);
+ }else if ($part>0){
+ if ((strcasecmp($a[0], "message")==0) && (strcasecmp($a[1], "rfc822")==0)){
+ $a = $a[8];
+ }
+ //echo "s - part: $part rest: $rest array: ".implode(" ", $a)."<br>\n";
+ if (is_array($a[$part-1])) return $a[$part-1];
+ else return false;
+ }else if (($part==0) || (empty($part))){
+ return $a;
+ }
+}
+
+function iml_GetNumParts($a, $part){
+ if (is_array($a)){
+ $parent=iml_GetPartArray($a, $part);
+
+ if ((strcasecmp($parent[0], "message")==0) && (strcasecmp($parent[1], "rfc822")==0)){
+ $parent = $parent[8];
+ }
+
+ $is_array=true;
+ $c=0;
+ while (( list ($key, $val) = each ($parent) )&&($is_array)){
+ $is_array=is_array($parent[$key]);
+ if ($is_array) $c++;
+ }
+ return $c;
+ }
+
+ return false;
+}
+
+function iml_GetPartTypeString($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])){
+ $type_str = "MULTIPART/";
+ reset($part_a);
+ while(list($n,$element)=each($part_a)){
+ if (!is_array($part_a[$n])){
+ $type_str.=$part_a[$n];
+ break;
+ }
+ }
+ return $type_str;
+ }else return $part_a[0]."/".$part_a[1];
+ }else return false;
+}
+
+function iml_GetFirstTextPart($structure,$part){
+ if ($part==0) $part="";
+ $typeCode = -1;
+ while ($typeCode!=0){
+ $typeCode = iml_GetPartTypeCode($structure, $part);
+ if ($typeCode == 1){
+ $part .= (empty($part)?"":".")."1";
+ }else if ($typeCode > 0){
+ $parts_a = explode(".", $part);
+ $lastPart = count($parts_a) - 1;
+ $parts_a[$lastPart] = (int)$parts_a[$lastPart] + 1;
+ $part = implode(".", $parts_a);
+ }else if ($typeCode == -1){
+ return "";
+ }
+ }
+
+ return $part;
+}
+
+function iml_GetPartTypeCode($a, $part){
+ $types=array(0=>"text",1=>"multipart",2=>"message",3=>"application",4=>"audio",5=>"image",6=>"video",7=>"other");
+
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) $str="multipart";
+ else $str=$part_a[0];
+
+ $code=7;
+ while ( list($key, $val) = each($types)) if (strcasecmp($val, $str)==0) $code=$key;
+ return $code;
+ }else return -1;
+}
+
+function iml_GetPartEncodingCode($a, $part){
+ $encodings=array("7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE", "OTHER");
+
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else $str=$part_a[5];
+
+ $code=5;
+ while ( list($key, $val) = each($encodings)) if (strcasecmp($val, $str)==0) $code=$key;
+
+ return $code;
+
+ }else return -1;
+}
+
+function iml_GetPartEncodingString($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else return $part_a[5];
+ }else return -1;
+}
+
+function iml_GetPartSize($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else return $part_a[6];
+ }else return -1;
+}
+
+function iml_GetPartID($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else return $part_a[3];
+ }else return -1;
+}
+
+function iml_GetPartDisposition($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else{
+ $id = count($part_a) - 2;
+ if (is_array($part_a[$id])) return $part_a[$id][0];
+ else return "";
+ }
+ }else return "";
+}
+
+function iml_GetPartName($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else{
+ $name = "";
+ if (is_array($part_a[2])){
+ //first look in content type
+ $name="";
+ while ( list($key, $val) = each ($part_a[2])){
+ if ((strcasecmp($val, "NAME")==0)||(strcasecmp($val, "FILENAME")==0))
+ $name=$part_a[2][$key+1];
+ }
+ }
+ if (empty($name)){
+ //check in content disposition
+ $id = count($part_a) - 2;
+ if ((is_array($part_a[$id])) && (is_array($part_a[$id][1]))){
+ $array = $part_a[$id][1];
+ while ( list($key, $val) = each($array)){
+ if ((strcasecmp($val, "NAME")==0)||(strcasecmp($val, "FILENAME")==0))
+ $name=$array[$key+1];
+ }
+ }
+ }
+ return $name;
+ }
+ }else return "";
+}
+
+
+function iml_GetPartCharset($a, $part){
+ $part_a=iml_GetPartArray($a, $part);
+ if ($part_a){
+ if (is_array($part_a[0])) return -1;
+ else{
+ if (is_array($part_a[2])){
+ $name="";
+ while ( list($key, $val) = each ($part_a[2])) if (strcasecmp($val, "charset")==0) $name=$part_a[2][$key+1];
+ return $name;
+ }
+ else return "";
+ }
+ }else return "";
+}
+
+function iml_GetPartList($a, $part){
+ //echo "MOO?"; flush();
+ $data = array();
+ $num_parts = iml_GetNumParts($a, $part);
+ //echo "($num_parts)"; flush();
+ if ($num_parts !== false){
+ //echo "<!-- ($num_parts parts)//-->\n";
+ for ($i = 0; $i<$num_parts; $i++){
+ $part_code = $part.(empty($part)?"":".").($i+1);
+ $part_type = iml_GetPartTypeCode($a, $part_code);
+ $part_disposition = iml_GetPartDisposition($a, $part_code);
+ //echo "<!-- part: $part_code type: $part_type //-->\n";
+ if (strcasecmp($part_disposition, "attachment")!=0 &&
+ (($part_type == 1) || ($part_type==2))){
+ $data = array_merge($data, iml_GetPartList($a, $part_code));
+ }else{
+ $data[$part_code]["typestring"] = iml_GetPartTypeString($a, $part_code);
+ $data[$part_code]["disposition"] = $part_disposition;
+ $data[$part_code]["size"] = iml_GetPartSize($a, $part_code);
+ $data[$part_code]["name"] = iml_GetPartName($a, $part_code);
+ $data[$part_code]["id"] = iml_GetPartID($a, $part_code);
+ }
+ }
+ }
+ return $data;
+}
+
+function iml_GetNextPart($part){
+ if (strpos($part, ".")===false) return $part++;
+ else{
+ $parts_a = explode(".", $part);
+ $num_levels = count($parts_a);
+ $parts_a[$num_levels-1]++;
+ return implode(".", $parts_a);
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+//
+//
+// utf7.inc - Routines to encode bytes to UTF7 and decode UTF7 strings
+//
+// Copyright (C) 1999, 2002 Ziberex and Torben Rybner
+//
+//
+// Version 1.01 2002-06-08 19:00
+//
+// - Adapted for use in IlohaMail (modified UTF-7 decoding)
+// - Converted from C to PHP4
+//
+//
+// Version 1.00 1999-09-03 19:00
+//
+// - Encodes bytes to UTF7 strings
+// *OutString = '\0';
+// StartBase64Encode();
+// for (CP = InString; *CP; CP++)
+// strcat(OutString, Base64Encode(*CP));
+// strcat(OutString, StopBase64Encode());
+// - Decodes Base64 strings to bytes
+// StartBase64Decode();
+// for (CP1 = InString, CP2 = OutString; *CP1 && (*CP1 != '='); CP1++)
+// CP2 += Base64Decode(*CP1, CP2);
+// StopBase64Decode();
+//
+
+$BASE64LENGTH = 60;
+
+$BASE64DECODE_NO_DATA = -1;
+$BASE64DECODE_EMPTY_DATA = -2;
+$BASE64DECODE_INVALID_DATA = -3;
+
+
+//
+//
+// Used for conversion to UTF7
+//
+$_ToUTF7 = array
+(
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', ','
+);
+
+//
+//
+// Used for conversion from UTF7
+// (0x80 => Illegal, 0x40 => CR/LF)
+//
+$_FromUTF7 = array
+(
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 00 - 07 - Ctrl -
+ 0x80, 0x80, 0x40, 0x80, 0x80, 0x40, 0x80, 0x80, // 08 - 0F - Ctrl -
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 10 - 17 - Ctrl -
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 18 - 1F - Ctrl -
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 20 - 27 !"#$%&'
+ 0x80, 0x80, 0x80, 0x3E, 0x3F, 0x80, 0x80, 0x3F, // 28 - 2F ()*+,-./
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, // 30 - 37 01234567
+ 0x3C, 0x3D, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, // 38 - 3F 89:;<=>?
+ 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 40 - 47 @ABCDEFG
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 48 - 4F HIJKLMNO
+ 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 50 - 57 PQRSTUVW
+ 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80, // 58 - 5F XYZ[\]^_
+ 0x80, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, // 60 - 67 `abcdefg
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 68 - 6F hijklmno
+ 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 70 - 77 pqrstuvw
+ 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, // 78 - 7F xyz{|}~
+);
+
+
+//
+//
+// UTF7EncodeInit:
+//
+// Start the encoding of bytes
+//
+function UTF7EncodeInit(&$Context)
+{
+ $Context[ "Data" ] = "";
+ $Context[ "Count" ] = 0;
+ $Context[ "Pos" ] = 0;
+ $Context[ "State" ] = 0;
+} // UTF7EncodeInit
+
+
+//
+//
+// UTF7EncodeByte:
+//
+// Encodes one byte to UTF7
+//
+function UTF7EncodeByte(&$Context, $Byte)
+{
+ global $_ToUTF7;
+
+ $Byte = ord($Byte);
+ switch ($Context[ "State" ])
+ {
+ case 0:
+ // Convert into a byte
+ $Context[ "Data" ] = $_ToUTF7[ $Byte >> 2 ];
+ $Context[ "Pos" ]++;
+ // Save residue for next converted byte
+ $Context[ "Residue" ] = ($Byte & 0x03) << 4;
+ // This is the first byte in this line
+ $Context[ "Count" ] = 1;
+ // Next state is 1
+ $Context[ "State" ] = 1;
+ break;
+
+ case 1:
+ // Convert into a byte
+ $Context[ "Data" ] .= $_ToUTF7[ $Context[ "Residue" ] | ($Byte >> 4) ];
+ $Context[ "Pos" ]++;
+ // Save residue for next converted byte
+ $Context[ "Residue" ] = ($Byte & 0x0F) << 2;
+ // Bumb byte counter
+ $Context[ "Count" ]++;
+ // Next state is 2
+ $Context[ "State" ] = 2;
+ break;
+
+ case 2:
+ // Convert into a byte
+ $Context[ "Data" ] .= $_ToUTF7[ $Context[ "Residue" ] | ($Byte >> 6) ];
+ $Context[ "Pos" ]++;
+ // Residue fits precisely into the next byte
+ $Context[ "Data" ] .= $_ToUTF7[ $Byte & 0x3F ];
+ $Context[ "Pos" ]++;
+ // Bumb byte counter
+ $Context[ "Count" ]++;
+ // Next state is 3
+ $Context[ "State" ] = 3;
+ break;
+
+ case 3:
+ // Convert into a byte
+ $Context[ "Data" ] .= $_ToUTF7[ $Byte >> 2 ];
+ $Context[ "Pos" ]++;
+ // Save residue for next converted byte
+ $Context[ "Residue" ] = ($Byte & 0x03) << 4;
+ // Bumb byte counter
+ $Context[ "Count" ]++;
+ // Next state is 1
+ $Context[ "State" ] = 1;
+ break;
+
+ default:
+ // printf("Internal error in UTF7Encode: State is %d\n", $Context[ "State" ]);
+ // exit(1);
+ break;
+ }
+} // UTF7EncodeByte
+
+
+//
+//
+// UTF7EncodeFinal:
+//
+// Terminates the encoding of bytes
+//
+function UTF7EncodeFinal(&$Context)
+{
+ if ($Context[ "State" ] == 0)
+ return "";
+ if ($Context[ "State" ] != 3)
+ UTF7EncodeByte($Context, "\0");
+ return $Context[ "Data" ];
+} // UTF7EncodeFinal
+
+
+//
+//
+// UTF7EncodeString
+//
+// Encodes a string to modified UTF-7 format
+//
+function UTF7EncodeString($String)
+{
+ // Not during encoding, yet
+ $Encoding = false;
+ // Go through the string
+ for ($I = 0; $I < strlen($String); $I++)
+ {
+ $Ch = substr($String, $I, 1);
+ if (ord($Ch) > 0x7F)
+ {
+ if (! $Encoding)
+ {
+ $RetVal .= "&";
+ $Encoding = true;
+ // Initialise UTF7 context
+ UTF7EncodeInit($Context);
+ }
+ UTF7EncodeByte($Context, "\0");
+ UTF7EncodeByte($Context, $Ch);
+ }
+ elseif ($Ch == "&")
+ {
+ if (! $Encoding)
+ {
+ $RetVal .= "&";
+ $Encoding = true;
+ // Initialise UTF7 context
+ UTF7EncodeInit($Context);
+ }
+ else
+ {
+ UTF7EncodeByte($Context, "\0");
+ UTF7EncodeByte($Context, $Ch);
+ }
+ }
+ else
+ {
+ if ($Encoding)
+ {
+ $RetVal .= UTF7EncodeFinal($Context) . "-$Ch";
+ $Encoding = false;
+ }
+ else
+ $RetVal .= $Ch;
+ }
+ }
+ if ($Encoding)
+ $RetVal .= UTF7EncodeFinal($Context) . "-";
+ return $RetVal;
+} // UTF7EncodeString
+
+
+//
+//
+// UTF7DecodeInit:
+//
+// Start the decoding of bytes
+//
+function UTF7DecodeInit(&$Context)
+{
+ $Context[ "Data" ] = "";
+ $Context[ "State" ] = 0;
+ $Context[ "Pos" ] = 0;
+} // UTF7DecodeInit
+
+
+//
+//
+// UTF7DecodeByte:
+//
+// Decodes one character from UTF7
+//
+function UTF7DecodeByte(&$Context, $Byte)
+{
+ global $BASE64DECODE_INVALID_DATA;
+ global $_FromUTF7;
+
+ // Restore bits
+ $Byte = $_FromUTF7[ ord($Byte) ];
+ // Ignore carriage returns and linefeeds
+ if ($Byte == 0x40)
+ return "";
+ // Invalid byte - Tell caller!
+ if ($Byte == 0x80)
+ $Context[ "Count" ] = $BASE64DECODE_INVALID_DATA;
+ switch ($Context[ "State" ])
+ {
+ case 0:
+ // Save residue
+ $Context[ "Residue" ] = $Byte;
+ // Initialise count
+ $Context[ "Count" ] = 0;
+ // Next state
+ $Context[ "State" ] = 1;
+ break;
+
+ case 1:
+ // Store byte
+ $Context[ "Data" ] .= chr(($Context[ "Residue" ] << 2) | ($Byte >> 4));
+ $Context[ "Pos" ]++;
+ // Update count
+ $Context[ "Count" ]++;
+ // Save residue
+ $Context[ "Residue" ] = $Byte;
+ // Next state
+ $Context[ "State" ] = 2;
+ break;
+
+ case 2:
+ // Store byte
+ $Context[ "Data" ] .= chr(($Context[ "Residue" ] << 4) | ($Byte >> 2));
+ $Context[ "Pos" ]++;
+ // Update count
+ $Context[ "Count" ]++;
+ // Save residue
+ $Context[ "Residue" ] = $Byte;
+ // Next state
+ $Context[ "State" ] = 3;
+ break;
+
+ case 3:
+ // Store byte
+ $Context[ "Data" ] .= chr(($Context[ "Residue" ] << 6) | $Byte);
+ $Context[ "Pos" ]++;
+ // Update count
+ $Context[ "Count" ]++;
+ // Next state
+ $Context[ "State" ] = 4;
+ break;
+
+ case 4:
+ // Save residue
+ $Context[ "Residue" ] = $Byte;
+ // Next state
+ $Context[ "State" ] = 1;
+ break;
+ }
+} // UTF7DecodeByte
+
+
+//
+//
+// UTF7DecodeFinal:
+//
+// Decodes one character from UTF7
+//
+function UTF7DecodeFinal(&$Context)
+{
+ // Buffer not empty - Return remainder!
+ if ($Context[ "Count" ])
+ {
+ $Context[ "Pos" ] = 0;
+ $Context[ "State" ] = 0;
+ return $Context[ "Data" ];
+ }
+ return "";
+} // UTF7DecodeFinal
+
+
+//
+//
+// UTF7DecodeString
+//
+// Converts a string encoded in modified UTF-7 encoding
+// to ISO 8859-1.
+// OBS: Works only for valid ISO 8859-1 characters in the
+// encoded data
+//
+function UTF7DecodeString($String)
+{
+ $Decoding = false;
+ for ($I = 0; $I < strlen($String); $I++)
+ {
+ $Ch = substr($String, $I, 1);
+ if ($Decoding)
+ {
+ if ($Ch == "-")
+ {
+ $RetVal .= UTF7DecodeFinal($Context);
+ $Decoding = false;
+ }
+ else
+ UTF7DecodeByte($Context, $Ch);
+ }
+ elseif ($Ch == "&")
+ {
+ if (($I < strlen($String) - 1) && (substr($String, $I + 1, 1) == "-"))
+ {
+ $RetVal .= $Ch;
+ $I++;
+ }
+ else
+ {
+ UTF7DecodeInit($Context);
+ $Decoding = true;
+ }
+ }
+ else
+ $RetVal .= $Ch;
+ }
+ return str_replace("\0", "", $RetVal);
+} // UTF7DecodeString
+?>
--- /dev/null
+<?php
+/*
+utf8 1.0
+Copyright: Left
+---------------------------------------------------------------------------------
+Version: 1.0
+Date: 23 November 2004
+---------------------------------------------------------------------------------
+Author: Alexander Minkovsky (a_minkovsky@hotmail.com)
+---------------------------------------------------------------------------------
+License: Choose the more appropriated for You - I don't care.
+---------------------------------------------------------------------------------
+Description:
+ Class provides functionality to convert single byte strings, such as CP1251
+ ti UTF-8 multibyte format and vice versa.
+ Class loads a concrete charset map, for example CP1251.
+ (Refer to ftp://ftp.unicode.org/Public/MAPPINGS/ for map files)
+ Directory containing MAP files is predefined as constant.
+ Each charset is also predefined as constant pointing to the MAP file.
+---------------------------------------------------------------------------------
+Example usage:
+ Pass the desired charset in the class constructor:
+ $utfConverter = new utf8(CP1251); //defaults to CP1250.
+ or load the charset MAP using loadCharset method like this:
+ $utfConverter->loadCharset(CP1252);
+ Then call
+ $res = $utfConverter->strToUtf8($str);
+ or
+ $res = $utfConverter->utf8ToStr($utf);
+ to get the needed encoding.
+---------------------------------------------------------------------------------
+Note:
+ Rewrite or Override the onError method if needed. It's the error handler used from everywhere and takes 2 parameters:
+ err_code and err_text. By default it just prints out a message about the error.
+*/
+
+// Charset maps
+// Adapted to fit RoundCube
+define("UTF8_MAP_DIR", "program/lib/encoding");
+$utf8_maps = array(
+ "CP1250" => UTF8_MAP_DIR . "/CP1250.map",
+ "CP1251" => UTF8_MAP_DIR . "/CP1251.map",
+ "CP1252" => UTF8_MAP_DIR . "/CP1252.map",
+ "CP1253" => UTF8_MAP_DIR . "/CP1253.map",
+ "CP1254" => UTF8_MAP_DIR . "/CP1254.map",
+ "CP1255" => UTF8_MAP_DIR . "/CP1255.map",
+ "CP1256" => UTF8_MAP_DIR . "/CP1256.map",
+ "CP1257" => UTF8_MAP_DIR . "/CP1257.map",
+ "CP1258" => UTF8_MAP_DIR . "/CP1258.map",
+ "ISO-8859-1" => UTF8_MAP_DIR . "/ISO-8859-1.map",
+ "ISO-8859-2" => UTF8_MAP_DIR . "/ISO-8859-2.map",
+ "ISO-8859-3" => UTF8_MAP_DIR . "/ISO-8859-3.map",
+ "ISO-8859-4" => UTF8_MAP_DIR . "/ISO-8859-4.map");
+
+//Error constants
+define("ERR_OPEN_MAP_FILE","ERR_OPEN_MAP_FILE");
+
+//Class definition
+Class utf8{
+
+ var $charset = "ISO-8859-1";
+ var $ascMap = array();
+ var $utfMap = array();
+
+ // made PHP5 capable by RoundCube
+ function __construct($charset="ISO-8859-1"){
+ $this->loadCharset($charset);
+ }
+
+ //Constructor
+ function utf8($charset="ISO-8859-1"){
+ $this->__construct($charset);
+ }
+
+ //Load charset
+ function loadCharset($charset){
+ global $utf8_maps;
+
+ if (!is_file($utf8_maps[$charset]))
+ {
+ $this->onError(ERR_OPEN_MAP_FILE, "Failed to open map file for $charset");
+ return;
+ }
+
+ if (empty($this->ascMap[$charset]))
+ {
+ $lines = file_get_contents($utf8_maps[$charset]);
+ $lines = preg_replace("/#.*$/m","",$lines);
+ $lines = preg_replace("/\n\n/","",$lines);
+ $lines = explode("\n",$lines);
+ foreach($lines as $line){
+ $parts = explode('0x',$line);
+ if(count($parts)==3){
+ $asc=hexdec(substr($parts[1],0,2));
+ $utf=hexdec(substr($parts[2],0,4));
+ $this->ascMap[$charset][$asc]=$utf;
+ }
+ }
+ }
+
+ $this->charset = $charset;
+ $this->utfMap = array_flip($this->ascMap[$charset]);
+ }
+
+ //Error handler
+ function onError($err_code,$err_text){
+ //print($err_code . " : " . $err_text . "<hr>\n");
+ raise_error(array('code' => 500,
+ 'file' => __FILE__,
+ 'message' => $err_text), TRUE, FALSE);
+ }
+
+ //Translate string ($str) to UTF-8 from given charset
+ function strToUtf8($str){
+ $chars = unpack('C*', $str);
+ $cnt = count($chars);
+ for($i=1;$i<=$cnt;$i++) $this->_charToUtf8($chars[$i]);
+ return implode("",$chars);
+ }
+
+ //Translate UTF-8 string to single byte string in the given charset
+ function utf8ToStr($utf){
+ $chars = unpack('C*', $utf);
+ $cnt = count($chars);
+ $res = ""; //No simple way to do it in place... concatenate char by char
+ for ($i=1;$i<=$cnt;$i++){
+ $res .= $this->_utf8ToChar($chars, $i);
+ }
+ return $res;
+ }
+
+ //Char to UTF-8 sequence
+ function _charToUtf8(&$char){
+ $c = (int)$this->ascMap[$this->charset][$char];
+ if ($c < 0x80){
+ $char = chr($c);
+ }
+ else if($c<0x800) // 2 bytes
+ $char = (chr(0xC0 | $c>>6) . chr(0x80 | $c & 0x3F));
+ else if($c<0x10000) // 3 bytes
+ $char = (chr(0xE0 | $c>>12) . chr(0x80 | $c>>6 & 0x3F) . chr(0x80 | $c & 0x3F));
+ else if($c<0x200000) // 4 bytes
+ $char = (chr(0xF0 | $c>>18) . chr(0x80 | $c>>12 & 0x3F) . chr(0x80 | $c>>6 & 0x3F) . chr(0x80 | $c & 0x3F));
+ }
+
+ //UTF-8 sequence to single byte character
+ function _utf8ToChar(&$chars, &$idx){
+ if(($chars[$idx] >= 240) && ($chars[$idx] <= 255)){ // 4 bytes
+ $utf = (intval($chars[$idx]-240) << 18) +
+ (intval($chars[++$idx]-128) << 12) +
+ (intval($chars[++$idx]-128) << 6) +
+ (intval($chars[++$idx]-128) << 0);
+ }
+ else if (($chars[$idx] >= 224) && ($chars[$idx] <= 239)){ // 3 bytes
+ $utf = (intval($chars[$idx]-224) << 12) +
+ (intval($chars[++$idx]-128) << 6) +
+ (intval($chars[++$idx]-128) << 0);
+ }
+ else if (($chars[$idx] >= 192) && ($chars[$idx] <= 223)){ // 2 bytes
+ $utf = (intval($chars[$idx]-192) << 6) +
+ (intval($chars[++$idx]-128) << 0);
+ }
+ else{ // 1 byte
+ $utf = $chars[$idx];
+ }
+ if(array_key_exists($utf,$this->utfMap))
+ return chr($this->utfMap[$utf]);
+ else
+ return "?";
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/de_CH/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ | Corrections: Alexander Stiebing <ja.stiebing[NOSPAM]@web.de> |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 282 2006-07-25 22:11:50Z thomasb $
+
+*/
+
+$labels = array();
+
+// login page // Login-Seite
+$labels['welcome'] = 'Willkommen bei $product';
+$labels['username'] = 'Benutzername';
+$labels['password'] = 'Passwort';
+$labels['server'] = 'Server';
+$labels['login'] = 'Login';
+
+// taskbar // Aktionsleiste
+$labels['logout'] = 'Logout';
+$labels['mail'] = 'E-Mail';
+$labels['settings'] = 'Einstellungen';
+$labels['addressbook'] = 'Adressbuch';
+
+// mailbox names // E-Mail-Ordnernamen
+$labels['inbox'] = 'Posteingang';
+$labels['drafts'] = 'Entwürfe';
+$labels['sent'] = 'Gesendet';
+$labels['trash'] = 'Gelöscht';
+$labels['junk'] = 'Junk';
+
+// message listing // Nachrichtenliste
+$labels['subject'] = 'Betreff';
+$labels['from'] = 'Absender';
+$labels['to'] = 'Empfänger';
+$labels['cc'] = 'Kopie (CC)';
+$labels['bcc'] = 'Blind-Kopie';
+$labels['replyto'] = 'Antwort an';
+$labels['date'] = 'Datum';
+$labels['size'] = 'Grösse';
+$labels['priority'] = 'Priorität';
+$labels['organization'] = 'Organisation';
+
+// aliases // [Platzhalter]
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Ordner';
+$labels['messagesfromto'] = 'Nachrichten $from bis $to von $count';
+$labels['messagenrof'] = 'Nachricht $nr von $count';
+
+$labels['moveto'] = 'Verschieben nach...';
+$labels['download'] = 'Download';
+
+$labels['filename'] = 'Dateiname';
+$labels['filesize'] = 'Dateigrösse';
+
+$labels['preferhtml'] = 'HTML bevorzugen';
+$labels['htmlmessage'] = 'HTML Nachricht';
+$labels['prettydate'] = 'Kurze Datumsanzeige';
+
+$labels['addtoaddressbook'] = 'Ins Adressbuch übernehmen';
+
+// weekdays short // Wochentage (Abkürzungen)
+$labels['sun'] = 'So';
+$labels['mon'] = 'Mo';
+$labels['tue'] = 'Di';
+$labels['wed'] = 'Mi';
+$labels['thu'] = 'Do';
+$labels['fri'] = 'Fr';
+$labels['sat'] = 'Sa';
+
+// weekdays long // Wochentage (normal)
+$labels['sunday'] = 'Sonntag';
+$labels['monday'] = 'Montag';
+$labels['tuesday'] = 'Dienstag';
+$labels['wednesday'] = 'Mittwoch';
+$labels['thursday'] = 'Donnerstag';
+$labels['friday'] = 'Freitag';
+$labels['saturday'] = 'Samstag';
+
+$labels['today'] = 'Heute';
+
+// toolbar buttons // Symbolleisten-Tipps
+$labels['checkmail'] = 'Überprüfung auf neue Anzeigen';
+$labels['writenewmessage'] = 'Neue Nachricht schreiben';
+$labels['replytomessage'] = 'Antwort verfassen';
+$labels['replytoallmessage'] = 'Antwort an Absender und alle Empfänger';
+$labels['forwardmessage'] = 'Nachricht weiterleiten';
+$labels['deletemessage'] = 'In den Papierkorb verschieben';
+$labels['printmessage'] = 'Diese Nachricht drucken';
+$labels['previousmessages'] = 'Vorherige Nachrichten anzeigen';
+$labels['nextmessages'] = 'Weitere Nachrichten anzeigen';
+$labels['backtolist'] = 'Zurück zur Liste';
+
+$labels['select'] = 'Auswählen';
+$labels['all'] = 'Alle';
+$labels['none'] = 'Keine';
+$labels['unread'] = 'Ungelesene';
+
+$labels['compact'] = 'Packen';
+$labels['empty'] = 'Leeren';
+$labels['purge'] = 'Aufräumen';
+
+$labels['quota'] = 'Verwendeter Speicherplatz';
+$labels['unknown'] = 'unbekannt';
+$labels['unlimited'] = 'unlimitiert';
+
+$labels['quicksearch'] = 'Schnellsuche';
+$labels['resetsearch'] = 'Löschen';
+
+
+// message compose // Nachrichten erstellen
+$labels['compose'] = 'Neue Nachricht verfassen';
+$labels['savemessage'] = 'Nachricht speichern';
+$labels['sendmessage'] = 'Nachricht jetzt senden';
+$labels['addattachment'] = 'Datei anfügen';
+$labels['charset'] = 'Zeichensatz';
+$labels['returnreceipt'] = 'Empfangsbestätigung';
+
+$labels['checkspelling'] = 'Rechtschreibung prüfen';
+$labels['resumeediting'] = 'Bearbeitung fortzetzen';
+$labels['revertto'] = 'Zurück zu';
+
+$labels['attachments'] = 'Anhänge';
+$labels['upload'] = 'Hochladen';
+$labels['close'] = 'Schliessen';
+
+$labels['low'] = 'Niedrig';
+$labels['lowest'] = 'Niedrigste';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'Hoch';
+$labels['highest'] = 'Höchste';
+
+$labels['nosubject'] = '(kein Betreff)';
+$labels['showimages'] = 'Bilder anzeigen';
+
+
+// address book // Adressbuch
+$labels['name'] = 'Anzeigename';
+$labels['firstname'] = 'Vorname';
+$labels['surname'] = 'Nachname';
+$labels['email'] = 'E-Mail';
+
+$labels['addcontact'] = 'Kontakt hinzufügen';
+$labels['editcontact'] = 'Kontakt bearbeiten';
+
+$labels['edit'] = 'Bearbeiten';
+$labels['cancel'] = 'Abbrechen';
+$labels['save'] = 'Speichern';
+$labels['delete'] = 'Löschen';
+
+$labels['newcontact'] = 'Neuen Kontakt erfassen';
+$labels['deletecontact'] = 'Gewählte Kontakte löschen';
+$labels['composeto'] = 'Nachricht verfassen';
+$labels['contactsfromto'] = 'Kontakte $from bis $to von $count';
+$labels['print'] = 'Drucken';
+$labels['export'] = 'Exportieren';
+
+$labels['previouspage'] = 'Eine Seite zurück';
+$labels['nextpage'] = 'Nächste Seite';
+
+// LDAP search
+$labels['ldapsearch'] = 'LDAP Verzeichnis-Suche';
+
+$labels['ldappublicsearchname'] = 'Kontakt-Name';
+$labels['ldappublicsearchtype'] = 'Genaue Übereinstimmung';
+$labels['ldappublicserverselect'] = 'Server-Auswahl';
+$labels['ldappublicsearchfield'] = 'Suche in';
+$labels['ldappublicsearchform'] = 'Adressen suchen';
+$labels['ldappublicsearch'] = 'Suchen';
+
+// settings // Einstellungen
+$labels['settingsfor'] = 'Einstellungen für';
+
+$labels['preferences'] = 'Einstellungen';
+$labels['userpreferences'] = 'Benutzereinstellungen';
+$labels['editpreferences'] = 'Einstellungen bearbeiten';
+
+$labels['identities'] = 'Absender';
+$labels['manageidentities'] = 'Absender für dieses Konto verwalten';
+$labels['newidentity'] = 'Neuer Absender';
+
+$labels['newitem'] = 'Neuer Eintrag';
+$labels['edititem'] = 'Eintrag bearbeiten';
+
+$labels['setdefault'] = 'Als Standard';
+$labels['language'] = 'Sprache';
+$labels['timezone'] = 'Zeitzone';
+$labels['pagesize'] = 'Einträge pro Seite';
+$labels['signature'] = 'Signatur';
+$labels['dstactive'] = 'Sommerzeit';
+
+$labels['folder'] = 'Ordner';
+$labels['folders'] = 'Ordner';
+$labels['foldername'] = 'Ordnername';
+$labels['subscribed'] = 'Abonniert';
+$labels['create'] = 'Erstellen';
+$labels['createfolder'] = 'Neuen Ordner erstellen';
+$labels['rename'] = 'Umbenennen';
+$labels['renamefolder'] = 'Ordner umbenennen';
+$labels['deletefolder'] = 'Ordner löschen';
+$labels['managefolders'] = 'Ordner verwalten';
+
+$labels['sortby'] = 'Sortieren nach';
+$labels['sortasc'] = 'aufsteigend sortieren';
+$labels['sortdesc'] = 'absteigend sortieren';
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/de_CH/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 285 2006-07-30 19:38:06Z thomasb $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Login fehlgeschlagen';
+
+$messages['cookiesdisabled'] = 'Ihr Browser akzeptiert keine Cookies';
+
+$messages['sessionerror'] = 'Ihre Session ist ungültig oder abgelaufen';
+
+$messages['imaperror'] = 'Keine Verbindung zum IMAP Server';
+
+$messages['nomessagesfound'] = 'Keine Nachrichten in diesem Ordner';
+
+$messages['loggedout'] = 'Sie haben Ihre Session erfolgreich beendet. Auf Wiedersehen!';
+
+$messages['mailboxempty'] = 'Ordner ist leer';
+
+$messages['loading'] = $messages['loadingdata'] = 'Daten werden geladen...';
+
+$messages['checkingmail'] = 'Überprüfung auf neue Nachrichten...';
+
+$messages['sendingmessage'] = 'Nachricht wird gesendet...';
+
+$messages['messagesent'] = 'Nachricht erfolgreich gesendet';
+
+$messages['savingmessage'] = 'Nachricht wird gespeichert...';
+
+$messages['messagesaved'] = 'Nachricht als Entwurf gespeichert';
+
+$messages['successfullysaved'] = 'Erfolgreich gespeichert';
+
+$messages['addedsuccessfully'] = 'Kontakt zum Adressbuch hinzugefügt';
+
+$messages['contactexists'] = 'Es existiert bereits ein Eintrag mit dieser E-Mail-Adresse';
+
+$messages['blockedimages'] = 'Um Ihre Privatsphäre zur schützen, wurden externe Bilder blockiert.';
+
+$messages['encryptedmessage'] = 'Dies ist eine verschlüsselte Nachricht und kann leider nicht angezeigt werden.';
+
+$messages['nocontactsfound'] = 'Keine Kontakte gefunden';
+
+$messages['sendingfailed'] = 'Versand der Nachricht fehlgeschlagen';
+
+$messages['errorsaving'] = 'Beim Speichern ist ein Fehler aufgetreten';
+
+$messages['errormoving'] = 'Nachricht konnte nicht verschoben werden';
+
+$messages['errordeleting'] = 'Nachricht konnte nicht gelöscht werden';
+
+$messages['deletecontactconfirm'] = 'Wollen Sie die ausgewählten Kontakte wirklich löschen';
+
+$messages['deletefolderconfirm'] = 'Wollen Sie diesen Ordner wirklich löschen?';
+
+$messages['purgefolderconfirm'] = 'Wollen Sie diesen Ordner wirklich leeren?';
+
+$messages['formincomplete'] = 'Das Formular wurde nicht vollständig ausgefüllt';
+
+$messages['noemailwarning'] = 'Bitte geben Sie eine gültige E-Mail-Adresse ein';
+
+$messages['nonamewarning'] = 'Bitte geben Sie einen Namen ein';
+
+$messages['nopagesizewarning'] = 'Bitte geben Sie eine Einträge pro Seite ein';
+
+$messages['norecipientwarning'] = 'Bitte geben Sie mindestens einen Empfänger an';
+
+$messages['nosubjectwarning'] = 'Die Betreffzeile ist leer. Möchten Sie jetzt einen Betreff eingeben?';
+
+$messages['nobodywarning'] = 'Diese Nachricht ohne Inhalt senden?';
+
+$messages['notsentwarning'] = 'Ihre Nachricht wurde nicht gesendet. Wollen Sie die Nachricht verwerfen?';
+
+$messages['noldapserver'] = 'Bitte wählen Sie einen LDAP-Server aus';
+
+$messages['nocontactsreturned'] = 'Es wurden keine Kontakte gefunden';
+
+$messages['nosearchname'] = 'Bitte geben Sie einen Namen oder eine E-Mail-Adresse ein';
+
+$messages['searchsuccessful'] = '$nr Nachrichten gefunden';
+
+$messages['searchnomatch'] = 'Keine Treffer';
+
+$messages['searching'] = 'Suche...';
+
+$messages['checking'] = 'Prüfe...';
+
+$messages['nospellerrors'] = 'Keine Rechtschreibfehler gefunden';
+
+$messages['folderdeleted'] = 'Ordner erfolgreich gelöscht';
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/de_DE/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+ | de_DE translation: Stephan Diehl <info@sd-edv.de> |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 282 2006-07-25 22:11:50Z thomasb $
+
+*/
+
+$labels = array();
+
+// login page // Login-Seite
+$labels['welcome'] = 'Willkommen bei $product';
+$labels['username'] = 'Benutzername';
+$labels['password'] = 'Passwort';
+$labels['server'] = 'Server';
+$labels['login'] = 'Anmelden';
+
+// taskbar // Aktionsleiste
+$labels['logout'] = 'Abmelden';
+$labels['mail'] = 'E-Mail';
+$labels['settings'] = 'Einstellungen';
+$labels['addressbook'] = 'Adressbuch';
+
+// mailbox names // E-Mail-Ordnernamen
+$labels['inbox'] = 'Posteingang';
+$labels['drafts'] = 'Entwürfe';
+$labels['sent'] = 'Gesendet';
+$labels['trash'] = 'Gelöscht';
+$labels['junk'] = 'Spam';
+
+// message listing // Nachrichtenliste
+$labels['subject'] = 'Betreff';
+$labels['from'] = 'Absender';
+$labels['to'] = 'Empfänger';
+$labels['cc'] = 'Kopie (CC)';
+$labels['bcc'] = 'Blind-Kopie';
+$labels['replyto'] = 'Antwort an';
+$labels['date'] = 'Datum';
+$labels['size'] = 'Größe';
+$labels['priority'] = 'Priorität';
+$labels['organization'] = 'Organisation';
+
+// aliases // [Platzhalter]
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Ordner';
+$labels['messagesfromto'] = 'Nachrichten $from bis $to von $count';
+$labels['messagenrof'] = 'Nachricht $nr von $count';
+
+$labels['moveto'] = 'Verschieben nach...';
+$labels['download'] = 'Download';
+
+$labels['filename'] = 'Dateiname';
+$labels['filesize'] = 'Dateigröße';
+
+$labels['preferhtml'] = 'HTML bevorzugen';
+$labels['htmlmessage'] = 'HTML Nachricht';
+$labels['prettydate'] = 'Kurze Datumsanzeige';
+
+$labels['addtoaddressbook'] = 'Ins Adressbuch übernehmen';
+
+// weekdays short // Wochentage (Abkürzungen)
+$labels['sun'] = 'So';
+$labels['mon'] = 'Mo';
+$labels['tue'] = 'Di';
+$labels['wed'] = 'Mi';
+$labels['thu'] = 'Do';
+$labels['fri'] = 'Fr';
+$labels['sat'] = 'Sa';
+
+// weekdays long // Wochentage (normal)
+$labels['sunday'] = 'Sonntag';
+$labels['monday'] = 'Montag';
+$labels['tuesday'] = 'Dienstag';
+$labels['wednesday'] = 'Mittwoch';
+$labels['thursday'] = 'Donnerstag';
+$labels['friday'] = 'Freitag';
+$labels['saturday'] = 'Samstag';
+
+$labels['today'] = 'Heute';
+
+// toolbar buttons // Symbolleisten-Tipps
+$labels['checkmail'] = 'Überprüfung auf neue Anzeigen';
+$labels['writenewmessage'] = 'Neue Nachricht schreiben';
+$labels['replytomessage'] = 'Antwort verfassen';
+$labels['replytoallmessage'] = 'Antwort an Absender und alle Empfänger';
+$labels['forwardmessage'] = 'Nachricht weiterleiten';
+$labels['deletemessage'] = 'In den Papierkorb verschieben';
+$labels['printmessage'] = 'Diese Nachricht drucken';
+$labels['previousmessages'] = 'Vorherige Nachrichten anzeigen';
+$labels['nextmessages'] = 'Weitere Nachrichten anzeigen';
+$labels['backtolist'] = 'Zurück zur Liste';
+
+$labels['select'] = 'Auswählen';
+$labels['all'] = 'Alle';
+$labels['none'] = 'Keine';
+$labels['unread'] = 'Ungelesene';
+
+$labels['compact'] = 'Packen';
+$labels['empty'] = 'Leeren';
+$labels['purge'] = 'Bereinigen';
+
+$labels['quota'] = 'Verwendeter Speicherplatz';
+$labels['unknown'] = 'unbekannt';
+$labels['unlimited'] = 'unlimitiert';
+
+$labels['quicksearch'] = 'Schnellsuche';
+$labels['resetsearch'] = 'Löschen';
+
+
+// message compose // Nachrichten erstellen
+$labels['compose'] = 'Neue Nachricht verfassen';
+$labels['savemessage'] = 'Nachricht speichern';
+$labels['sendmessage'] = 'Nachricht jetzt senden';
+$labels['addattachment'] = 'Datei anfügen';
+$labels['charset'] = 'Zeichensatz';
+$labels['returnreceipt'] = 'Empfangsbestätigung';
+
+$labels['checkspelling'] = 'Rechtschreibung prüfen';
+$labels['resumeediting'] = 'Bearbeitung fortzetzen';
+$labels['revertto'] = 'Zurück zu';
+
+$labels['attachments'] = 'Anhänge';
+$labels['upload'] = 'Hochladen';
+$labels['close'] = 'Schließen';
+
+$labels['low'] = 'Niedrig';
+$labels['lowest'] = 'Niedrigste';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'Hoch';
+$labels['highest'] = 'Höchste';
+
+$labels['nosubject'] = '(kein Betreff)';
+$labels['showimages'] = 'Bilder anzeigen';
+
+
+// address book // Adressbuch
+$labels['name'] = 'Anzeigename';
+$labels['firstname'] = 'Vorname';
+$labels['surname'] = 'Nachname';
+$labels['email'] = 'E-Mail';
+
+$labels['addcontact'] = 'Kontakt hinzufügen';
+$labels['editcontact'] = 'Kontakt bearbeiten';
+
+$labels['edit'] = 'Bearbeiten';
+$labels['cancel'] = 'Abbrechen';
+$labels['save'] = 'Speichern';
+$labels['delete'] = 'Löschen';
+
+$labels['newcontact'] = 'Neuen Kontakt erfassen';
+$labels['deletecontact'] = 'Gewählte Kontakte löschen';
+$labels['composeto'] = 'Nachricht verfassen';
+$labels['contactsfromto'] = 'Kontakte $from bis $to von $count';
+$labels['print'] = 'Drucken';
+$labels['export'] = 'Exportieren';
+
+$labels['previouspage'] = 'Eine Seite zurück';
+$labels['nextpage'] = 'Nächste Seite';
+
+// LDAP search
+$labels['ldapsearch'] = 'LDAP Verzeichnis-Suche';
+
+$labels['ldappublicsearchname'] = 'Kontakt-Name';
+$labels['ldappublicsearchtype'] = 'Genaue Übereinstimmung';
+$labels['ldappublicserverselect'] = 'Server-Auswahl';
+$labels['ldappublicsearchfield'] = 'Suche in';
+$labels['ldappublicsearchform'] = 'Adressen suchen';
+$labels['ldappublicsearch'] = 'Suchen';
+
+// settings // Einstellungen
+$labels['settingsfor'] = 'Einstellungen für';
+
+$labels['preferences'] = 'Einstellungen';
+$labels['userpreferences'] = 'Benutzereinstellungen';
+$labels['editpreferences'] = 'Einstellungen bearbeiten';
+
+$labels['identities'] = 'Absender';
+$labels['manageidentities'] = 'Absender für dieses Konto verwalten';
+$labels['newidentity'] = 'Neuer Absender';
+
+$labels['newitem'] = 'Neuer Eintrag';
+$labels['edititem'] = 'Eintrag bearbeiten';
+
+$labels['setdefault'] = 'Als Standard';
+$labels['language'] = 'Sprache';
+$labels['timezone'] = 'Zeitzone';
+$labels['pagesize'] = 'Einträge pro Seite';
+$labels['signature'] = 'Signatur';
+$labels['dstactive'] = 'Sommerzeit';
+
+$labels['folder'] = 'Ordner';
+$labels['folders'] = 'Ordner';
+$labels['foldername'] = 'Ordnername';
+$labels['subscribed'] = 'Abonniert';
+$labels['create'] = 'Erstellen';
+$labels['createfolder'] = 'Neuen Ordner erstellen';
+$labels['rename'] = 'Umbenennen';
+$labels['renamefolder'] = 'Ordner umbenennen';
+$labels['deletefolder'] = 'Ordner löschen';
+$labels['managefolders'] = 'Ordner verwalten';
+
+$labels['sortby'] = 'Sortieren nach';
+$labels['sortasc'] = 'aufsteigend sortieren';
+$labels['sortdesc'] = 'absteigend sortieren';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/de_DE/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+ | de_DE translation: Stephan Diehl <info@sd-edv.de> |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 285 2006-07-30 19:38:06Z thomasb $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Anmelden fehlgeschlagen';
+
+$messages['cookiesdisabled'] = 'Ihr Browser akzeptiert keine Cookies';
+
+$messages['sessionerror'] = 'Ihre Session ist ungültig oder abgelaufen';
+
+$messages['imaperror'] = 'Keine Verbindung zum IMAP Server';
+
+$messages['nomessagesfound'] = 'Keine Nachrichten in diesem Ordner';
+
+$messages['loggedout'] = 'Sie haben Ihre Session erfolgreich beendet. Auf Wiedersehen!';
+
+$messages['mailboxempty'] = 'Ordner ist leer';
+
+$messages['loading'] = $messages['loadingdata'] = 'Daten werden geladen...';
+
+$messages['checkingmail'] = 'Überprüfung auf neue Nachrichten...';
+
+$messages['sendingmessage'] = 'Nachricht wird gesendet...';
+
+$messages['messagesent'] = 'Nachricht erfolgreich gesendet';
+
+$messages['savingmessage'] = 'Nachricht wird gespeichert...';
+
+$messages['messagesaved'] = 'Nachricht als Entwurf gespeichert';
+
+$messages['successfullysaved'] = 'Erfolgreich gespeichert';
+
+$messages['addedsuccessfully'] = 'Kontakt zum Adressbuch hinzugefügt';
+
+$messages['contactexists'] = 'Es existiert bereits ein Eintrag mit dieser E-Mail-Adresse';
+
+$messages['blockedimages'] = 'Um Ihre Privatsphäre zur schützen, wurden externe Bilder blockiert.';
+
+$messages['encryptedmessage'] = 'Dies ist eine verschlüsselte Nachricht und kann leider nicht angezeigt werden.';
+
+$messages['nocontactsfound'] = 'Keine Kontakte gefunden';
+
+$messages['sendingfailed'] = 'Versand der Nachricht fehlgeschlagen';
+
+$messages['errorsaving'] = 'Beim Speichern ist ein Fehler aufgetreten';
+
+$messages['errormoving'] = 'Nachricht konnte nicht verschoben werden';
+
+$messages['errordeleting'] = 'Nachricht konnte nicht gelöscht werden';
+
+$messages['deletecontactconfirm'] = 'Wollen Sie die ausgewählten Kontakte wirklich löschen';
+
+$messages['deletefolderconfirm'] = 'Wollen Sie diesen Ordner wirklich löschen?';
+
+$messages['purgefolderconfirm'] = 'Wollen Sie diesen Ordner wirklich leeren?';
+
+$messages['formincomplete'] = 'Das Formular wurde nicht vollständig ausgefüllt';
+
+$messages['noemailwarning'] = 'Bitte geben Sie eine gültige E-Mail-Adresse ein';
+
+$messages['nonamewarning'] = 'Bitte geben Sie einen Namen ein';
+
+$messages['nopagesizewarning'] = 'Bitte geben Sie eine Einträge pro Seite ein';
+
+$messages['norecipientwarning'] = 'Bitte geben Sie mindestens einen Empfänger an';
+
+$messages['nosubjectwarning'] = 'Die Betreffzeile ist leer. Möchten Sie jetzt einen Betreff eingeben?';
+
+$messages['nobodywarning'] = 'Diese Nachricht ohne Inhalt senden?';
+
+$messages['notsentwarning'] = 'Ihre Nachricht wurde nicht gesendet. Wollen Sie die Nachricht verwerfen?';
+
+$messages['noldapserver'] = 'Bitte wählen Sie einen LDAP-Server aus';
+
+$messages['nocontactsreturned'] = 'Es wurden keine Kontakte gefunden';
+
+$messages['nosearchname'] = 'Bitte geben Sie einen Namen oder eine E-Mail-Adresse ein';
+
+$messages['searchsuccessful'] = '$nr Nachrichten gefunden';
+
+$messages['searchnomatch'] = 'Keine Treffer';
+
+$messages['searching'] = 'Suche...';
+
+$messages['checking'] = 'Prüfe...';
+
+$messages['nospellerrors'] = 'Keine Rechtschreibfehler gefunden';
+
+$messages['folderdeleted'] = 'Ordner erfolgreich gelöscht';
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/en_GB/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Weiran Zhang (weiran@weiran.co.uk) |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 296 2006-08-06 15:53:41Z thomasb $
+
+*/
+
+$labels = array();
+
+// login page
+$labels['welcome'] = 'Welcome to $product';
+$labels['username'] = 'Username';
+$labels['password'] = 'Password';
+$labels['server'] = 'Server';
+$labels['login'] = 'Login';
+
+// taskbar
+$labels['logout'] = 'Logout';
+$labels['mail'] = 'E-Mail';
+$labels['settings'] = 'Personal Settings';
+$labels['addressbook'] = 'Address Book';
+
+// mailbox names
+$labels['inbox'] = 'Inbox';
+$labels['drafts'] = 'Drafts';
+$labels['sent'] = 'Sent';
+$labels['trash'] = 'Deleted Items';
+$labels['drafts'] = 'Drafts';
+$labels['junk'] = 'Junk';
+
+// message listing
+$labels['subject'] = 'Subject';
+$labels['from'] = 'Sender';
+$labels['to'] = 'Recipient';
+$labels['cc'] = 'Copy';
+$labels['bcc'] = 'Bcc';
+$labels['replyto'] = 'Reply-To';
+$labels['date'] = 'Date';
+$labels['size'] = 'Size';
+$labels['priority'] = 'Priority';
+$labels['organization'] = 'Organisation';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Folders';
+$labels['messagesfromto'] = 'Messages $from to $to of $count';
+$labels['messagenrof'] = 'Message $nr of $count';
+
+$labels['moveto'] = 'move to...';
+$labels['download'] = 'download';
+
+$labels['filename'] = 'File name';
+$labels['filesize'] = 'File size';
+
+$labels['preferhtml'] = 'Prefer HTML';
+$labels['htmlmessage'] = 'HTML Message';
+$labels['prettydate'] = 'Pretty dates';
+
+$labels['addtoaddressbook'] = 'Add to address book';
+
+// weekdays short
+$labels['sun'] = 'Sun';
+$labels['mon'] = 'Mon';
+$labels['tue'] = 'Tue';
+$labels['wed'] = 'Wed';
+$labels['thu'] = 'Thu';
+$labels['fri'] = 'Fri';
+$labels['sat'] = 'Sat';
+
+// weekdays long
+$labels['sunday'] = 'Sunday';
+$labels['monday'] = 'Monday';
+$labels['tuesday'] = 'Tuesday';
+$labels['wednesday'] = 'Wednesday';
+$labels['thursday'] = 'Thursday';
+$labels['friday'] = 'Friday';
+$labels['saturday'] = 'Saturday';
+
+$labels['today'] = 'Today';
+
+// toolbar buttons
+$labels['checkmail'] = 'Check for new messages';
+$labels['writenewmessage'] = 'Create a new message';
+$labels['replytomessage'] = 'Reply to the message';
+$labels['replytoallmessage'] = 'Reply to sender and all recipients';
+$labels['forwardmessage'] = 'Forward the message';
+$labels['deletemessage'] = 'Move message to trash';
+$labels['printmessage'] = 'Print this message';
+$labels['previousmessages'] = 'Show previous set of messages';
+$labels['nextmessages'] = 'Show next set of messages';
+$labels['backtolist'] = 'Back to message list';
+$labels['viewsource'] = 'Show source';
+
+$labels['select'] = 'Select';
+$labels['all'] = 'All';
+$labels['none'] = 'None';
+$labels['unread'] = 'Unread';
+
+$labels['compact'] = 'Compact';
+$labels['empty'] = 'Empty';
+$labels['purge'] = 'Purge';
+
+$labels['quota'] = 'Disk usage';
+
+
+// message compose
+$labels['compose'] = 'Compose a message';
+$labels['savemessage'] = 'Save this draft';
+$labels['sendmessage'] = 'Send the message now';
+$labels['addattachment'] = 'Attach a file';
+$labels['charset'] = 'Charset';
+
+$labels['attachments'] = 'Attachments';
+$labels['upload'] = 'Upload';
+$labels['close'] = 'Close';
+
+$labels['low'] = 'Low';
+$labels['lowest'] = 'Lowest';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'High';
+$labels['highest'] = 'Highest';
+
+$labels['nosubject'] = '(no subject)';
+$labels['showimages'] = 'Display images';
+
+
+// address boook
+$labels['name'] = 'Display name';
+$labels['firstname'] = 'First name';
+$labels['surname'] = 'Last name';
+$labels['email'] = 'E-Mail';
+
+$labels['addcontact'] = 'Add new contact';
+$labels['editcontact'] = 'Edit contact';
+
+$labels['edit'] = 'Edit';
+$labels['cancel'] = 'Cancel';
+$labels['save'] = 'Save';
+$labels['delete'] = 'Delete';
+
+$labels['newcontact'] = 'Create new contact card';
+$labels['deletecontact'] = 'Delete selected contacts';
+$labels['composeto'] = 'Compose mail to';
+$labels['contactsfromto'] = 'Contacts $from to $to of $count';
+$labels['print'] = 'Print';
+$labels['export'] = 'Export';
+
+// LDAP search
+$labels['ldapsearch'] = 'LDAP directory search';
+
+$labels['ldappublicsearchname'] = 'Contact name';
+$labels['ldappublicsearchtype'] = 'Exact match?';
+$labels['ldappublicserverselect'] = 'Select servers';
+$labels['ldappublicsearchfield'] = 'Search on';
+$labels['ldappublicsearchform'] = 'Look for a contact';
+$labels['ldappublicsearch'] = 'Search';
+
+// settings
+$labels['settingsfor'] = 'Settings for';
+
+$labels['preferences'] = 'Preferences';
+$labels['userpreferences'] = 'User preferences';
+$labels['editpreferences'] = 'Edit user preferences';
+
+$labels['identities'] = 'Identities';
+$labels['manageidentities'] = 'Manage identities for this account';
+$labels['newidentity'] = 'New identity';
+
+$labels['newitem'] = 'New item';
+$labels['edititem'] = 'Edit item';
+
+$labels['setdefault'] = 'Set default';
+$labels['language'] = 'Language';
+$labels['timezone'] = 'Time zone';
+$labels['pagesize'] = 'Rows per page';
+$labels['signature'] = 'Signature';
+
+$labels['folder'] = 'Folder';
+$labels['folders'] = 'Folders';
+$labels['foldername'] = 'Folder name';
+$labels['subscribed'] = 'Subscribed';
+$labels['create'] = 'Create';
+$labels['createfolder'] = 'Create new folder';
+$labels['deletefolder'] = 'Delete folder';
+$labels['managefolders'] = 'Manage folders';
+
+$labels['sortby'] = 'Sort by';
+$labels['sortasc'] = 'Sort ascending';
+$labels['sortdesc'] = 'Sort descending';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/en_GB/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Weiran Zhang (weiran@weiran.co.uk) |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 285 2006-07-30 19:38:06Z thomasb $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Login failed';
+
+$messages['cookiesdisabled'] = 'Your browser does not accept cookies';
+
+$messages['sessionerror'] = 'Your session is invalid or expired';
+
+$messages['imaperror'] = 'Connection to IMAP server failed';
+
+$messages['nomessagesfound'] = 'No messages found in this mailbox';
+
+$messages['loggedout'] = 'You have successfully terminated the session. Good bye!';
+
+$messages['mailboxempty'] = 'Mailbox is empty';
+
+$messages['loading'] = 'Loading...';
+
+$messages['loadingdata'] = 'Loading data...';
+
+$messages['checkingmail'] = 'Checking for new messages...';
+
+$messages['messagesent'] = 'Message sent successfully';
+
+$messages['savingmessage'] = 'Saving message...';
+
+$messages['messagesaved'] = 'Message saved to Drafts';
+
+$messages['successfullysaved'] = 'Successfully saved';
+
+$messages['addedsuccessfully'] = 'Contact added successfully to address book';
+
+$messages['contactexists'] = 'A contact with this e-mail address already exists';
+
+$messages['blockedimages'] = 'To protect your privacy, remote images are blocked in this message.';
+
+$messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!';
+
+$messages['nocontactsfound'] = 'No contacts found';
+
+$messages['sendingfailed'] = 'Failed to send message';
+
+$messages['errorsaving'] = 'An error occured while saving';
+
+$messages['errormoving'] = 'Could not move the message';
+
+$messages['errordeleting'] = 'Could not delete the message';
+
+$messages['deletecontactconfirm'] = 'Do you really want to delete the selected contact(s)?';
+
+$messages['deletefolderconfirm'] = 'Do you really want to delete this folder?';
+
+$messages['formincomplete'] = 'The form was not completely filled out';
+
+$messages['noemailwarning'] = 'Please enter a valid email address';
+
+$messages['nonamewarning'] = 'Please enter a name';
+
+$messages['nopagesizewarning'] = 'Please enter a page size';
+
+$messages['norecipientwarning'] = 'Please enter at least one recipient';
+
+$messages['nosubjectwarning'] = 'The "Subject" field is empty. Would you like to enter one now?';
+
+$messages['nobodywarning'] = 'Send this message without text?';
+
+$messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?';
+
+$messages['noldapserver'] = 'Please select an ldap server to search';
+
+$messages['nocontactsreturned'] = 'No contacts were found';
+
+$messages['nosearchname'] = 'Please enter a contact name or email address';
+
+$messages['searchsuccessful'] = '$nr messages found';
+
+$messages['searchnomatch'] = 'Search returned no matches';
+
+$messages['searching'] = 'Searching...';
+
+$messages['checking'] = 'Checking...';
+
+$messages['nospellerrors'] = 'No spelling errors found';
+
+$messages['folderdeleted'] = 'Folder successfully deleted';
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/en_US/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 296 2006-08-06 15:53:41Z thomasb $
+
+*/
+
+$labels = array();
+
+// login page
+$labels['welcome'] = 'Welcome to $product';
+$labels['username'] = 'Username';
+$labels['password'] = 'Password';
+$labels['server'] = 'Server';
+$labels['login'] = 'Login';
+
+// taskbar
+$labels['logout'] = 'Logout';
+$labels['mail'] = 'E-Mail';
+$labels['settings'] = 'Personal Settings';
+$labels['addressbook'] = 'Address Book';
+
+// mailbox names
+$labels['inbox'] = 'Inbox';
+$labels['drafts'] = 'Drafts';
+$labels['sent'] = 'Sent';
+$labels['trash'] = 'Trash';
+$labels['junk'] = 'Junk';
+
+// message listing
+$labels['subject'] = 'Subject';
+$labels['from'] = 'Sender';
+$labels['to'] = 'Recipient';
+$labels['cc'] = 'Copy';
+$labels['bcc'] = 'Bcc';
+$labels['replyto'] = 'Reply-To';
+$labels['date'] = 'Date';
+$labels['size'] = 'Size';
+$labels['priority'] = 'Priority';
+$labels['organization'] = 'Organization';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Folders';
+$labels['messagesfromto'] = 'Messages $from to $to of $count';
+$labels['messagenrof'] = 'Message $nr of $count';
+
+$labels['moveto'] = 'move to...';
+$labels['download'] = 'download';
+
+$labels['filename'] = 'File name';
+$labels['filesize'] = 'File size';
+
+$labels['preferhtml'] = 'Prefer HTML';
+$labels['htmlmessage'] = 'HTML Message';
+$labels['prettydate'] = 'Pretty dates';
+
+$labels['addtoaddressbook'] = 'Add to address book';
+
+// weekdays short
+$labels['sun'] = 'Sun';
+$labels['mon'] = 'Mon';
+$labels['tue'] = 'Tue';
+$labels['wed'] = 'Wed';
+$labels['thu'] = 'Thu';
+$labels['fri'] = 'Fri';
+$labels['sat'] = 'Sat';
+
+// weekdays long
+$labels['sunday'] = 'Sunday';
+$labels['monday'] = 'Monday';
+$labels['tuesday'] = 'Tuesday';
+$labels['wednesday'] = 'Wednesday';
+$labels['thursday'] = 'Thursday';
+$labels['friday'] = 'Friday';
+$labels['saturday'] = 'Saturday';
+
+$labels['today'] = 'Today';
+
+// toolbar buttons
+$labels['checkmail'] = 'Check for new messages';
+$labels['writenewmessage'] = 'Create a new message';
+$labels['replytomessage'] = 'Reply to the message';
+$labels['replytoallmessage'] = 'Reply to sender and all recipients';
+$labels['forwardmessage'] = 'Forward the message';
+$labels['deletemessage'] = 'Move message to trash';
+$labels['printmessage'] = 'Print this message';
+$labels['previousmessages'] = 'Show previous set of messages';
+$labels['nextmessages'] = 'Show next set of messages';
+$labels['backtolist'] = 'Back to message list';
+$labels['viewsource'] = 'Show source';
+
+$labels['select'] = 'Select';
+$labels['all'] = 'All';
+$labels['none'] = 'None';
+$labels['unread'] = 'Unread';
+
+$labels['compact'] = 'Compact';
+$labels['empty'] = 'Empty';
+$labels['purge'] = 'Purge';
+
+$labels['quota'] = 'Disk usage';
+$labels['unknown'] = 'unknown';
+$labels['unlimited'] = 'unlimited';
+
+$labels['quicksearch'] = 'Quick search';
+$labels['resetsearch'] = 'Reset search';
+
+
+// message compose
+$labels['compose'] = 'Compose a message';
+$labels['savemessage'] = 'Save this draft';
+$labels['sendmessage'] = 'Send the message now';
+$labels['addattachment'] = 'Attach a file';
+$labels['charset'] = 'Charset';
+$labels['returnreceipt'] = 'Return receipt';
+
+$labels['checkspelling'] = 'Check spelling';
+$labels['resumeediting'] = 'Resume editing';
+$labels['revertto'] = 'Revert to';
+
+$labels['attachments'] = 'Attachments';
+$labels['upload'] = 'Upload';
+$labels['close'] = 'Close';
+
+$labels['low'] = 'Low';
+$labels['lowest'] = 'Lowest';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'High';
+$labels['highest'] = 'Highest';
+
+$labels['nosubject'] = '(no subject)';
+$labels['showimages'] = 'Display images';
+
+
+// address boook
+$labels['name'] = 'Display name';
+$labels['firstname'] = 'First name';
+$labels['surname'] = 'Last name';
+$labels['email'] = 'E-Mail';
+
+$labels['addcontact'] = 'Add new contact';
+$labels['editcontact'] = 'Edit contact';
+
+$labels['edit'] = 'Edit';
+$labels['cancel'] = 'Cancel';
+$labels['save'] = 'Save';
+$labels['delete'] = 'Delete';
+
+$labels['newcontact'] = 'Create new contact card';
+$labels['deletecontact'] = 'Delete selected contacts';
+$labels['composeto'] = 'Compose mail to';
+$labels['contactsfromto'] = 'Contacts $from to $to of $count';
+$labels['print'] = 'Print';
+$labels['export'] = 'Export';
+
+$labels['previouspage'] = 'Show previous set';
+$labels['nextpage'] = 'Show next set';
+
+
+// LDAP search
+$labels['ldapsearch'] = 'LDAP directory search';
+
+$labels['ldappublicsearchname'] = 'Contact name';
+$labels['ldappublicsearchtype'] = 'Exact match?';
+$labels['ldappublicserverselect'] = 'Select servers';
+$labels['ldappublicsearchfield'] = 'Search on';
+$labels['ldappublicsearchform'] = 'Look for a contact';
+$labels['ldappublicsearch'] = 'Search';
+
+
+// settings
+$labels['settingsfor'] = 'Settings for';
+
+$labels['preferences'] = 'Preferences';
+$labels['userpreferences'] = 'User preferences';
+$labels['editpreferences'] = 'Edit user preferences';
+
+$labels['identities'] = 'Identities';
+$labels['manageidentities'] = 'Manage identities for this account';
+$labels['newidentity'] = 'New identity';
+
+$labels['newitem'] = 'New item';
+$labels['edititem'] = 'Edit item';
+
+$labels['setdefault'] = 'Set default';
+$labels['language'] = 'Language';
+$labels['timezone'] = 'Time zone';
+$labels['pagesize'] = 'Rows per page';
+$labels['signature'] = 'Signature';
+$labels['dstactive'] = 'Daylight savings';
+
+$labels['folder'] = 'Folder';
+$labels['folders'] = 'Folders';
+$labels['foldername'] = 'Folder name';
+$labels['subscribed'] = 'Subscribed';
+$labels['create'] = 'Create';
+$labels['createfolder'] = 'Create new folder';
+$labels['rename'] = 'Rename';
+$labels['renamefolder'] = 'Rename folder';
+$labels['deletefolder'] = 'Delete folder';
+$labels['managefolders'] = 'Manage folders';
+
+$labels['sortby'] = 'Sort by';
+$labels['sortasc'] = 'Sort ascending';
+$labels['sortdesc'] = 'Sort descending';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/en/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 285 2006-07-30 19:38:06Z thomasb $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Login failed';
+
+$messages['cookiesdisabled'] = 'Your browser does not accept cookies';
+
+$messages['sessionerror'] = 'Your session is invalid or expired';
+
+$messages['imaperror'] = 'Connection to IMAP server failed';
+
+$messages['nomessagesfound'] = 'No messages found in this mailbox';
+
+$messages['loggedout'] = 'You have successfully terminated the session. Good bye!';
+
+$messages['mailboxempty'] = 'Mailbox is empty';
+
+$messages['loading'] = 'Loading...';
+
+$messages['loadingdata'] = 'Loading data...';
+
+$messages['checkingmail'] = 'Checking for new messages...';
+
+$messages['sendingmessage'] = 'Sending message...';
+
+$messages['messagesent'] = 'Message sent successfully';
+
+$messages['savingmessage'] = 'Saving message...';
+
+$messages['messagesaved'] = 'Message saved to Drafts';
+
+$messages['successfullysaved'] = 'Successfully saved';
+
+$messages['addedsuccessfully'] = 'Contact added successfully to address book';
+
+$messages['contactexists'] = 'A contact with this e-mail address already exists';
+
+$messages['blockedimages'] = 'To protect your privacy, remote images are blocked in this message.';
+
+$messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!';
+
+$messages['nocontactsfound'] = 'No contacts found';
+
+$messages['sendingfailed'] = 'Failed to send message';
+
+$messages['errorsaving'] = 'An error occured while saving';
+
+$messages['errormoving'] = 'Could not move the message';
+
+$messages['errordeleting'] = 'Could not delete the message';
+
+$messages['deletecontactconfirm'] = 'Do you really want to delete the selected contact(s)?';
+
+$messages['deletefolderconfirm'] = 'Do you really want to delete this folder?';
+
+$messages['purgefolderconfirm'] = 'Do you really want to delete all messages in this folder?';
+
+$messages['formincomplete'] = 'The form was not completely filled out';
+
+$messages['noemailwarning'] = 'Please enter a valid email address';
+
+$messages['nonamewarning'] = 'Please enter a name';
+
+$messages['nopagesizewarning'] = 'Please enter a page size';
+
+$messages['norecipientwarning'] = 'Please enter at least one recipient';
+
+$messages['nosubjectwarning'] = 'The "Subject" field is empty. Would you like to enter one now?';
+
+$messages['nobodywarning'] = 'Send this message without text?';
+
+$messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?';
+
+$messages['noldapserver'] = 'Please select an ldap server to search';
+
+$messages['nocontactsreturned'] = 'No contacts were found';
+
+$messages['nosearchname'] = 'Please enter a contact name or email address';
+
+$messages['searchsuccessful'] = '$nr messages found';
+
+$messages['searchnomatch'] = 'Search returned no matches';
+
+$messages['searching'] = 'Searching...';
+
+$messages['checking'] = 'Checking...';
+
+$messages['nospellerrors'] = 'No spelling errors found';
+
+$messages['folderdeleted'] = 'Folder successfully deleted';
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/es/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: David Grajal Blanco <dgrabla@gmail.com> |
+ | http://david.grajal.net |
+ +-----------------------------------------------------------------------+
+ | Changelog: |
+ | - 6/2/2006 Translations of new features and improvements) |
+ | - 17/9/2005 First release |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 282 2006-07-25 22:11:50Z thomasb $
+
+*/
+
+$labels = array();
+
+// login page
+$labels['welcome'] = 'Bienvenido a $product';
+$labels['username'] = 'Nombre de usuario';
+$labels['password'] = 'Contraseña';
+$labels['server'] = 'Servidor';
+$labels['login'] = 'Entrar';
+
+// taskbar
+$labels['logout'] = 'Cerrar sesión';
+$labels['mail'] = 'E-Mail';
+$labels['settings'] = 'Configuración';
+$labels['addressbook'] = 'Contactos';
+
+// mailbox names
+$labels['inbox'] = 'Entrada';
+$labels['drafts'] = 'Borradores';
+$labels['sent'] = 'Enviados';
+$labels['trash'] = 'Papelera';
+$labels['junk'] = 'Basura';
+
+// message listing
+$labels['subject'] = 'Asunto';
+$labels['from'] = 'Remitente';
+$labels['to'] = 'Destinatario';
+$labels['cc'] = 'Copia';
+$labels['bcc'] = 'Bcc';
+$labels['replyto'] = 'Responder';
+$labels['date'] = 'Fecha';
+$labels['size'] = 'Tamaño';
+$labels['priority'] = 'Prioridad';
+$labels['organization'] = 'Organización';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Carpetas';
+$labels['messagesfromto'] = 'Mensajes desde $from a $to de $count';
+$labels['messagenrof'] = 'Mensaje $nr de $count';
+
+$labels['moveto'] = 'mover a...';
+$labels['download'] = 'descargar';
+
+$labels['filename'] = 'Nombre del fichero';
+$labels['filesize'] = 'Tamaño del fichero';
+
+$labels['preferhtml'] = 'Prefiero HTML';
+$labels['htmlmessage'] = 'Mensaje HTML';
+$labels['prettydate'] = 'Fecha detallada';
+
+$labels['addtoaddressbook'] = 'Añadir a contactos';
+
+// weekdays short
+$labels['sun'] = 'Dom';
+$labels['mon'] = 'Lun';
+$labels['tue'] = 'Mar';
+$labels['wed'] = 'Mie';
+$labels['thu'] = 'Jue';
+$labels['fri'] = 'Vie';
+$labels['sat'] = 'Sáb';
+
+// weekdays long
+$labels['sunday'] = 'Domingo';
+$labels['monday'] = 'Lunes';
+$labels['tuesday'] = 'Martes';
+$labels['wednesday'] = 'Miercoles';
+$labels['thursday'] = 'Jueves';
+$labels['friday'] = 'Viernes';
+$labels['saturday'] = 'Sábado';
+
+$labels['today'] = 'Hoy';
+
+// toolbar buttons
+$labels['checkmail'] = 'Revisar si hay nuevos mensajes';
+$labels['writenewmessage'] = 'Crear nuevo mensaje';
+$labels['replytomessage'] = 'Responder al mensaje';
+$labels['replytoallmessage'] = 'Responder al emisor y a todos los destinatarios';
+$labels['forwardmessage'] = 'Reenviar mensaje';
+$labels['deletemessage'] = 'Mover mensaje a la papelera';
+$labels['printmessage'] = 'Imprimir este mensaje';
+$labels['previousmessages'] = 'Mostrar mensajes anteriores';
+$labels['nextmessages'] = 'Mostrar mensajes siguientes';
+$labels['backtolist'] = 'Volver a la lista de mensajes';
+
+$labels['viewsource'] = 'Mostrar código';
+
+$labels['select'] = 'Seleccionar';
+$labels['all'] = 'Todos';
+$labels['none'] = 'Ninguno';
+$labels['unread'] = 'No leidos';
+
+$labels['compact'] = 'Compactar';
+$labels['empty'] = 'Vaciar';
+$labels['purge'] = 'Eliminar';
+
+$labels['quota'] = 'Uso de disco';
+$labels['unknown'] = 'desconocido';
+$labels['unlimited'] = 'sin límite';
+
+$labels['quicksearch'] = 'Búsqueda rápida';
+$labels['resetsearch'] = 'Reajustar la búsqueda';
+
+
+// message compose
+$labels['compose'] = 'Escribir un mensaje';
+$labels['savemessage'] = 'Almacenar como borrador';
+$labels['sendmessage'] = 'Enviar ahora el mensaje';
+$labels['addattachment'] = 'Añadir un fichero';
+$labels['charset'] = 'Codigo';
+$labels['returnreceipt'] = 'Recibo de entrega';
+
+$labels['checkspelling'] = 'Revisar la ortografía';
+$labels['resumeediting'] = 'Continuar el editaje';
+$labels['revertto'] = 'Revertir a';
+
+$labels['attachments'] = 'Adjuntos';
+$labels['upload'] = 'Subir';
+$labels['close'] = 'Cerrar';
+
+$labels['low'] = 'Bajo';
+$labels['lowest'] = 'Bajísimo';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'Alto';
+$labels['highest'] = 'Altísimo';
+
+
+$labels['nosubject'] = '(sin asunto)';
+$labels['showimages'] = 'Mostrar imágenes';
+
+
+// address boook
+$labels['name'] = 'Nombre completo';
+$labels['firstname'] = 'Nombre';
+$labels['surname'] = 'Apellido';
+$labels['email'] = 'E-Mail';
+
+$labels['edit'] = 'Editar';
+$labels['cancel'] = 'Cancelar';
+$labels['save'] = 'Almacenar';
+$labels['delete'] = 'Eliminar';
+
+$labels['newcontact'] = 'Crear nuevo contacto';
+$labels['addcontact'] = 'Añadir nuevo contacto';
+$labels['editcontact'] = 'Editar contacto';
+$labels['deletecontact'] = 'Eliminar contactos seleccionados';
+$labels['composeto'] = 'Redactar correo a';
+$labels['contactsfromto'] = 'Contactos $from a $to de $count';
+$labels['print'] = 'Imprimir';
+$labels['export'] = 'Exportar';
+
+$labels['previouspage'] = 'Mostrar grupo anterior';
+$labels['nextpage'] = 'Mostrar grupo siguiente';
+
+// LDAP search
+$labels['ldapsearch'] = 'Búsqueda en el directorio LDAP';
+
+$labels['ldappublicsearchname'] = 'Nombre';
+$labels['ldappublicsearchtype'] = '¿Búsqueda exacta?';
+$labels['ldappublicserverselect'] = 'Elegir servidores';
+$labels['ldappublicsearchfield'] = 'Buscando';
+$labels['ldappublicsearchform'] = 'Buscar un contacto';
+$labels['ldappublicsearch'] = 'Buscar';
+
+
+// settings
+$labels['settingsfor'] = 'Configuración para';
+
+$labels['preferences'] = 'Preferencias';
+$labels['userpreferences'] = 'Preferencias de usuario';
+$labels['editpreferences'] = 'Editar preferencias de usuario';
+
+$labels['identities'] = 'Identidades';
+$labels['manageidentities'] = 'Gestionar identidades para esta cuenta';
+$labels['newidentity'] = 'Nueva identidad';
+
+$labels['newitem'] = 'Nuevo';
+$labels['edititem'] = 'Editar';
+
+$labels['setdefault'] = 'Seleccionar opción por defecto';
+$labels['language'] = 'Idioma';
+$labels['timezone'] = 'Zona horaria';
+$labels['pagesize'] = 'Filas por página';
+$labels['signature'] = 'Firma';
+$labels['dstactive'] = 'Cambio de horario';
+
+$labels['folder'] = 'Carpeta';
+$labels['folders'] = 'Carpetas';
+$labels['foldername'] = 'Nombre de carpeta';
+$labels['subscribed'] = 'Suscribirse';
+$labels['create'] = 'Crear';
+$labels['createfolder'] = 'Crear nueva carpeta';
+$labels['rename'] = 'Renombrar';
+$labels['renamefolder'] = 'Renombrar carpeta';
+$labels['deletefolder'] = 'Eliminar carpeta';
+$labels['managefolders'] = 'Gestionar carpetas';
+
+$labels['sortby'] = 'Ordenar por';
+$labels['sortasc'] = 'Orden ascendente';
+$labels['sortdesc'] = 'Orden descendente';
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/es/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: David Grajal Blanco <dgrabla@gmail.com> |
+ | http://david.grajal.net |
+ +-----------------------------------------------------------------------+
+ | Changelog: |
+ | - 6/2/2006 Translations of new features and improvements |
+ | - 17/9/2005 First release |
+ +-----------------------------------------------------------------------+
+
+
+ $Id: messages.inc 282 2006-07-25 22:11:50Z thomasb $
+
+*/
+
+$messages = array();
+
+
+$messages['loginfailed'] = 'Contraseña incorrecta';
+
+$messages['cookiesdisabled'] = 'Su navegador no acepta cookies';
+
+$messages['sessionerror'] = 'Su sesión no existe o ha expirado';
+
+$messages['imaperror'] = 'Fallo de conexión con el servidor IMAP';
+
+$messages['nomessagesfound'] = 'No se han encontrado mensajes en este buzón';
+
+$messages['loggedout'] = 'Ha cerrado la sesión. ¡Hasta pronto!';
+
+$messages['mailboxempty'] = 'El buzón esta vacio';
+
+$messages['loading'] = 'Cargando...';
+
+$messages['loadingdata'] = 'Cargando datos...';
+
+$messages['checkingmail'] = 'Verificar si hay nuevos mensajes...';
+
+$messages['sendingmessage'] = 'Enviando mensaje...';
+
+$messages['messagesent'] = 'Mensaje enviado correctamente';
+
+$messages['savingmessage'] = 'Guardar mensaje...';
+
+$messages['messagesaved'] = 'Mensaje guardado en los bosquejos';
+
+$messages['successfullysaved'] = 'Guardado correctamente';
+
+$messages['addedsuccessfully'] = 'Contacto añadido correctamente a la libreta de direcciones';
+
+$messages['contactexists'] = 'Ya existe un contacto con esta dirección de correo';
+
+$messages['blockedimages'] = 'Para proteger su privacidad, las imágenes externas han sido bloqueadas en este mensaje';
+
+$messages['encryptedmessage'] = 'Este es un mensaje cifrado y no puede ser mostrado. ¡Lo siento!';
+
+$messages['nocontactsfound'] = 'No hay contactos';
+
+$messages['sendingfailed'] = 'Error al enviar mensaje';
+
+$messages['errorsaving'] = 'Ocurrió un error mientras se guardaba';
+
+$messages['errormoving'] = 'No se ha podido mover el mensaje';
+
+$messages['errordeleting'] = 'No se ha podido eliminar el mensaje';
+
+$messages['deletecontactconfirm'] = '¿Realmente quiere eliminar los contactos seleccionados?';
+
+$messages['deletefolderconfirm'] = '¿Esta seguro de que quiere eliminar esta carpeta?';
+
+$messages['purgefolderconfirm'] = '¿Esta seguro de que quiere eliminar todos los mensajes de esta carpeta?';
+
+$messages['formincomplete'] = 'No han sido rellenados todos los campos del formulario';
+
+$messages['noemailwarning'] = 'Por favor, introduzca un email válido';
+
+$messages['nonamewarning'] = 'Por favor, introduzca su nombre';
+
+$messages['nopagesizewarning'] = 'Por favor, introduzca un tamaño de página';
+
+$messages['norecipientwarning'] = 'Por favor, introduzca al menos un destinatario';
+
+$messages['nosubjectwarning'] = 'El campo "Asunto" esta vacio. ¿Desea redactarlo en este momento?';
+
+$messages['nobodywarning'] = '¿Quiere enviar este mensaje sin texto?';
+
+$messages['notsentwarning'] = 'El mensaje no ha sido enviado. ¿Desea descartar su mensaje?';
+
+$messages['noldapserver'] = 'Por favor, seleccione un servidor LDAP para buscar';
+
+$messages['nocontactsreturned'] = 'No se han encontrado contactos';
+
+$messages['nosearchname'] = 'Por favor, introduzca un nombre o la dirección email';
+
+$messages['searchsuccessful'] = 'Se encontró $nr mensajes';
+
+$messages['searchnomatch'] = 'La busqueda no obtuvo resultados';
+
+$messages['searching'] = 'Buscando...';
+
+$messages['checking'] = 'Revisando...';
+
+$messages['nospellerrors'] = 'No se encontró errores ortográficos';
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/fr/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: aldweb <info@aldweb.com>, Pierre HAEGELI <pierre@haegeli.net> |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 220 2006-06-12 17:33:21Z roundcube $
+
+*/
+
+$labels = array();
+
+// login page
+$labels['welcome'] = 'Bienvenue à $product';
+$labels['username'] = 'ID utilisateur';
+$labels['password'] = 'Mot de passe';
+$labels['server'] = 'Serveur';
+$labels['login'] = 'Connexion';
+
+// taskbar
+$labels['logout'] = 'Quitter';
+$labels['mail'] = 'e-Mail';
+$labels['settings'] = 'Préférences';
+$labels['addressbook'] = 'Carnet d\'adresses';
+
+// mailbox names
+$labels['inbox'] = 'Boîte de réception';
+$labels['sent'] = 'Messages envoyés';
+$labels['trash'] = 'Corbeille';
+$labels['drafts'] = 'Brouillons';
+$labels['junk'] = 'A trier';
+
+// message listing
+$labels['subject'] = 'Objet';
+$labels['from'] = 'De';
+$labels['to'] = 'A';
+$labels['cc'] = 'Cc';
+$labels['bcc'] = 'Cci';
+$labels['replyto'] = 'Répondre à';
+$labels['date'] = 'Date';
+$labels['size'] = 'Taille';
+$labels['priority'] = 'Priorité';
+$labels['organization'] = 'Organisation';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Dossiers';
+$labels['messagesfromto'] = 'Messages $from à $to sur $count';
+$labels['messagenrof'] = 'Message $nr sur $count';
+
+$labels['moveto'] = 'Déplacer vers...';
+$labels['download'] = 'Télécharger';
+
+$labels['filename'] = 'Nom du fichier';
+$labels['filesize'] = 'Taille du fichier';
+
+$labels['preferhtml'] = 'Préférer HTML';
+$labels['htmlmessage'] = 'Message HTML';
+$labels['prettydate'] = 'Affichage réduit des dates';
+
+$labels['addtoaddressbook'] = 'Ajouter au carnet d\'adresses';
+
+// weekdays short
+$labels['sun'] = 'Dim';
+$labels['mon'] = 'Lun';
+$labels['tue'] = 'Mar';
+$labels['wed'] = 'Mer';
+$labels['thu'] = 'Jeu';
+$labels['fri'] = 'Ven';
+$labels['sat'] = 'Sam';
+
+// weekdays long
+$labels['sunday'] = 'Dimanche';
+$labels['monday'] = 'Lundi';
+$labels['tuesday'] = 'Mardi';
+$labels['wednesday'] = 'Mercredi';
+$labels['thursday'] = 'Jeudi';
+$labels['friday'] = 'Vendredi';
+$labels['saturday'] = 'Samedi';
+
+$labels['today'] = 'Aujourd\'hui';
+
+// toolbar buttons
+$labels['checkmail'] = 'Vérification des nouveaux messages';
+$labels['writenewmessage'] = 'Créer un nouveau message';
+$labels['replytomessage'] = 'Répondre au message';
+$labels['replytoallmessage'] = 'Répondre à tous';
+$labels['forwardmessage'] = 'Transmettre le message';
+$labels['deletemessage'] = 'Déplacer le message dans la corbeille';
+$labels['printmessage'] = 'Imprimer ce message';
+$labels['previousmessages'] = 'Voir les messages précédents';
+$labels['nextmessages'] = 'Voir les messages suivants';
+$labels['backtolist'] = 'Retourner à la liste des messages';
+$labels['viewsource'] = 'Voir le code source';
+
+$labels['select'] = 'Sélectionner';
+$labels['all'] = 'Tous';
+$labels['none'] = 'Aucun';
+$labels['unread'] = 'Non lus';
+
+//$labels['compact'] = 'Compresser';
+$labels['empty'] = 'Vider';
+$labels['purge'] = 'Purger';
+
+$labels['quota'] = 'Utilisation Disque';
+$labels['unknown'] = 'inconnue';
+$labels['unlimited'] = 'illimitée';
+
+$labels['quicksearch'] = 'Recherche rapide';
+$labels['resetsearch'] = 'Réinitialiser la recherche';
+
+
+// message compose
+$labels['compose'] = 'Composer un nouveau message';
+$labels['savemessage'] = 'Sauvegarder ce brouillon';
+$labels['sendmessage'] = 'Envoyer le message maintenant';
+$labels['addattachment'] = 'Joindre un fichier';
+$labels['charset'] = 'Encodage';
+$labels['returnreceipt'] = 'Accusé de réception';
+
+$labels['checkspelling'] = 'Vérifier l\'orthographe';
+$labels['resumeediting'] = 'Retourner à l\'édition';
+$labels['revertto'] = 'Revenir à';
+
+$labels['attachments'] = 'Fichiers joints';
+$labels['upload'] = 'Joindre';
+$labels['close'] = 'Fermer';
+
+$labels['low'] = 'Basse';
+$labels['lowest'] = 'La plus basse';
+$labels['normal'] = 'Normale';
+$labels['high'] = 'Elevée';
+$labels['highest'] = 'La plus élevée';
+
+$labels['nosubject'] = '(pas de sujet)';
+$labels['showimages'] = 'Montrer les images';
+
+
+// address boook
+$labels['name'] = 'Nom à afficher';
+$labels['firstname'] = 'Prénom';
+$labels['surname'] = 'Nom';
+$labels['email'] = 'e-Mail';
+
+$labels['addcontact'] = 'Ajouter un nouveau contact';
+$labels['editcontact'] = 'Editer le contact';
+
+$labels['edit'] = 'Editer';
+$labels['cancel'] = 'Annuler';
+$labels['save'] = 'Sauvegarder';
+$labels['delete'] = 'Supprimer';
+
+$labels['newcontact'] = 'Créer un nouveau contact';
+$labels['addcontact'] = 'Ajouter le contact sélectionné à votre Carnet d\'adresses';
+$labels['deletecontact'] = 'Supprimer les contacts sélectionnés';
+$labels['composeto'] = 'Ecrire un message à';
+$labels['contactsfromto'] = 'Contacts $from à $to sur $count';
+$labels['print'] = 'Imprimer';
+$labels['export'] = 'Exporter';
+
+$labels['previouspage'] = 'Montrer page précédente';
+$labels['nextpage'] = 'Montrer page suivante';
+
+
+// LDAP search
+$labels['ldapsearch'] = 'Recherche dans répertoires LDAP';
+
+$labels['ldappublicsearchname'] = 'Nom du contact';
+$labels['ldappublicsearchtype'] = 'Correspondance exacte ?';
+$labels['ldappublicserverselect'] = 'Sélectionnez les serveurs';
+$labels['ldappublicsearchfield'] = 'Recherche sur';
+$labels['ldappublicsearchform'] = 'Chercher un contact';
+$labels['ldappublicsearch'] = 'Recherche';
+
+
+// settings
+$labels['settingsfor'] = 'Paramètres pour';
+
+$labels['preferences'] = 'Préférences';
+$labels['userpreferences'] = 'Préférences utilisateur';
+$labels['editpreferences'] = 'Editer les préférences utilisateur';
+
+$labels['identities'] = 'Identités';
+$labels['manageidentities'] = 'Gérer les identités pour ce compte';
+$labels['newidentity'] = 'Nouvelle identité';
+
+$labels['newitem'] = 'Nouvel élément';
+$labels['edititem'] = 'Editer l\'élément';
+
+$labels['setdefault'] = 'Paramètres par défaut';
+$labels['language'] = 'Langue';
+$labels['timezone'] = 'Fuseau horaire';
+$labels['pagesize'] = 'Nombre de lignes par page';
+$labels['signature'] = 'Signature';
+$labels['dstactive'] = 'Heure d\'été';
+
+$labels['folder'] = 'Dossier';
+$labels['folders'] = 'Dossiers';
+$labels['foldername'] = 'Nom du dossier';
+$labels['subscribed'] = 'Abonné';
+$labels['create'] = 'Créer';
+$labels['createfolder'] = 'Créer un nouveau dossier';
+$labels['rename'] = 'Renommer';
+$labels['renamefolder'] = 'Renommer le dossier';
+$labels['deletefolder'] = 'Supprimer le dossier';
+$labels['managefolders'] = 'Gérer les dossiers';
+
+$labels['sortby'] = 'Trier par';
+$labels['sortasc'] = 'Tri ascendant';
+$labels['sortdesc'] = 'Tri descendant';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/fr/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: aldweb <info@aldweb.com>, Pierre HAEGELI <pierre@haegeli.net> |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 139 2006-06-12 17:32:27Z roundcube $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Erreur de connexion';
+
+$messages['cookiesdisabled'] = 'Votre navigateur n\'accepte pas les cookies';
+
+$messages['sessionerror'] = 'Votre session n\'est pas valide ou a expiré';
+
+$messages['imaperror'] = 'Erreur de connexion au serveur IMAP';
+
+$messages['nomessagesfound'] = 'Aucun message trouvé dans cette boîte aux lettres';
+
+$messages['loggedout'] = 'Vous venez de vous déconnecter avec succès. Au revoir !';
+
+$messages['mailboxempty'] = 'Boîte aux lettres vide';
+
+$messages['loading'] = 'Chargement en cours...';
+
+$messages['loadingdata'] = 'Chargement des données en cours...';
+
+$messages['checkingmail'] = 'Vérification des nouveaux messages ...';
+
+$messages['sendingmessage'] = 'Message en cours d\'envoi...';
+
+$messages['messagesent'] = 'Message envoyé';
+
+$messages['savingmessage'] = 'Sauvegarde du message...';
+
+$messages['messagesaved'] = 'Message sauvegardé dans Brouillons';
+
+$messages['successfullysaved'] = 'Sauvegarde effectuée';
+
+$messages['addedsuccessfully'] = 'Contact ajouté dans le carnet d\'adresses';
+
+$messages['contactexists'] = 'Un contact avec cette adresse e-Mail existe déjà';
+
+$messages['blockedimages'] = 'Afin de préserver votre vie privée, les images distantes ont été bloquées dans ce message.';
+
+$messages['encryptedmessage'] = 'Ceci est un message crypté et il ne peut pas être affiché. Désolé !';
+
+$messages['nocontactsfound'] = 'Aucun contact trouvé';
+
+$messages['sendingfailed'] = 'Le message n\'a pas été envoyé';
+
+$messages['errorsaving'] = 'Une erreur est apparue pendant la sauvegarde';
+
+$messages['errormoving'] = 'Impossible de déplacer le message';
+
+$messages['errordeleting'] = 'Impossible d\'effacer le message';
+
+$messages['deletecontactconfirm'] = 'Voulez-vous vraiment effacer les contacts sélectionnés ?';
+
+$messages['deletefolderconfirm'] = 'Voulez-vous vraiment effacer ce dossier ?';
+
+$messages['purgefolderconfirm'] = 'Voulez-vous vraiment effacer tous les messages dans ce dossier ?';
+
+$messages['formincomplete'] = 'Le formulaire n\'a pas été entièrement rempli';
+
+$messages['noemailwarning'] = 'Entrez une adresse e-Mail valide SVP';
+
+$messages['nonamewarning'] = 'Entrez un nom SVP';
+
+$messages['nopagesizewarning'] = 'Entrez une taille de page SVP';
+
+$messages['norecipientwarning'] = 'Entrez au moins un destinataire SVP';
+
+$messages['nosubjectwarning'] = 'Le champ "Objet" est vide. Souhaitez-vous le compléter maintenant ?';
+
+$messages['nobodywarning'] = 'Envoyer ce message sans texte ?';
+
+$messages['notsentwarning'] = 'Le message n\'a pas été envoyé. Voulez-vous abandonner ce message ?';
+
+$messages['noldapserver'] = 'Choisissez un serveur ldap pour la recherche';
+
+$messages['nocontactsreturned'] = 'Aucun contact trouvé';
+
+$messages['nosearchname'] = 'Entrez un nom ou une adresse e-Mail de contact';
+
+$messages['searchsuccessful'] = '$nr messages trouvés';
+
+$messages['searchnomatch'] = 'Aucun résultat trouvé';
+
+$messages['searching'] = 'En cours de recherche...';
+
+$messages['checking'] = 'Vérification...';
+
+$messages['nospellerrors'] = 'Aucune faute trouvée';
+
+$messages['folderdeleted'] = 'Dossier effacé';
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/localization/index.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev, - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide centralized location for keeping track of |
+ | available languages |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: index.inc 258 2006-06-02 18:16:48Z thomasb $
+
+ */
+
+ $rcube_languages = array(
+ 'en_US' => 'English (US)',
+ 'en_GB' => 'English (GB)',
+ 'ar' => 'Arabic',
+ 'am' => 'Armenian',
+ 'bg' => 'Bulgarian',
+ 'bs_BA' => 'Bosnian (Bosanski)',
+ 'tw' => 'Chinese (BIG5)',
+ 'cn' => 'Chinese (GB2312)',
+ 'cz' => 'Czech',
+ 'ca' => 'Català',
+ 'da' => 'Dansk',
+ 'de_DE' => 'Deutsch (Deutsch)',
+ 'de_CH' => 'Deutsch (Schweiz)',
+ 'es' => 'Español',
+ 'et_EE' => 'Estonian',
+ 'eu' => 'Euskara',
+ 'fr' => 'Français',
+ 'ga' => 'Galician',
+ 'el' => 'Greek',
+ 'hr' => 'Hrvatski',
+ 'hu' => 'Hungarian',
+ 'it' => 'Italiano',
+ 'ja' => 'Japanese (日本語)',
+ 'kr' => 'Korean',
+ 'lv' => 'Latvian',
+ 'lt' => 'Lithuanian',
+ 'nl_NL' => 'Nederlands',
+ 'nl_BE' => 'Flemish',
+ 'nb_NO' => 'Norsk (bokmål)',
+ 'nn_NO' => 'Norsk (nynorsk)',
+ 'fa' => 'Persian',
+ 'pl' => 'Polski',
+ 'pt_PT' => 'Portuguese (Standard)',
+ 'pt_BR' => 'Portuguese (Brazilian)',
+ 'ro' => 'Romanian',
+ 'ru' => 'Russian',
+ 'fi' => 'Suomi',
+ 'se' => 'Svenska',
+ 'si' => 'Slovenian',
+ 'sk' => 'Slovak',
+ 'tr' => 'Turkish',
+ 'vn' => 'Vietnamese'
+);
+
+$rcube_language_aliases = array(
+ 'ee' => 'et_EE',
+ 'bs' => 'bs_BA',
+ 'de' => 'de_DE',
+ 'en' => 'en_US',
+ 'nl' => 'nl_NL',
+ 'no' => 'nn_NO',
+ 'pt' => 'pt_PT'
+);
+
+$rcube_charsets = array();
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/pt_BR/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Anderson S. Ferreira <anderson@cnpm.embrapa.br> |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 296 2006-08-06 15:53:41Z thomasb $
+
+*/
+
+$labels = array();
+
+// Página de login
+$labels['username'] = 'Usuário';
+$labels['password'] = 'Senha';
+$labels['server'] = 'Servidor';
+$labels['login'] = 'Entrar';
+
+// taskbar
+$labels['logout'] = 'Sair';
+$labels['mail'] = 'E-mail';
+$labels['settings'] = 'Configurações pessoais';
+$labels['addressbook'] = 'Catálogo de endereços';
+
+// Nome das pastas de correio
+$labels['inbox'] = 'Caixa de entrada';
+$labels['sent'] = 'Enviados';
+$labels['trash'] = 'Lixeira';
+$labels['drafts'] = 'Rascunhos';
+$labels['junk'] = 'Junk';
+
+// message listing
+$labels['subject'] = 'Assunto';
+$labels['from'] = 'Remetente';
+$labels['to'] = 'Para';
+$labels['cc'] = 'Cópia';
+$labels['bcc'] = 'Bcc';
+$labels['replyto'] = 'Responder para';
+$labels['date'] = 'Data';
+$labels['size'] = 'Tamanho';
+$labels['priority'] = 'Prioridade';
+$labels['organization'] = 'Organização';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Pastas';
+$labels['messagesfromto'] = 'Mensagens $from - $to de $count';
+$labels['messagenrof'] = 'Mensagem $nr de $count';
+
+$labels['moveto'] = 'mover para...';
+$labels['download'] = 'download';
+
+$labels['filename'] = 'Arquivo';
+$labels['filesize'] = 'Tamanho';
+
+$labels['preferhtml'] = 'Prefere HTML';
+$labels['htmlmessage'] = 'Mensagem HTML';
+$labels['prettydate'] = 'Formatar datas';
+
+$labels['addtoaddressbook'] = 'Incluir no catálogo de endereços';
+
+// Dias da semana abreviados
+$labels['sun'] = 'Dom';
+$labels['mon'] = 'Seg';
+$labels['tue'] = 'Ter';
+$labels['wed'] = 'Qua';
+$labels['thu'] = 'Qui';
+$labels['fri'] = 'Sex';
+$labels['sat'] = 'Sáb';
+
+// Dias da semana completos
+$labels['sunday'] = 'Domingo';
+$labels['monday'] = 'Segunda-feira';
+$labels['tuesday'] = 'Terça-feira';
+$labels['wednesday'] = 'Quarta-feira';
+$labels['thursday'] = 'Quinta-feira';
+$labels['friday'] = 'Sexta-feira';
+$labels['saturday'] = 'Sábado';
+
+$labels['today'] = 'Hoje';
+
+// toolbar buttons
+$labels['writenewmessage'] = 'Criar nova mensagem';
+$labels['replytomessage'] = 'Responder';
+$labels['replytoallmessage'] = 'Responder para todos';
+$labels['forwardmessage'] = 'Encaminhar';
+$labels['deletemessage'] = 'Mover para lixeira';
+$labels['printmessage'] = 'Imprimir';
+$labels['previousmessages'] = 'Anterior';
+$labels['nextmessages'] = 'Próximo';
+$labels['backtolist'] = 'Voltar';
+$labels['viewsource'] = 'Exibir código fonte';
+
+$labels['select'] = 'Selecionar';
+$labels['all'] = 'Todas';
+$labels['none'] = 'Nenhuma';
+$labels['unread'] = 'Não lidas';
+
+$labels['compact'] = 'Compactar';
+$labels['empty'] = 'Vazio';
+$labels['purge'] = 'Apagar';
+
+$labels['quota'] = 'Utilização';
+
+
+// message compose
+$labels['compose'] = 'Escrever mensagem';
+$labels['sendmessage'] = 'Enviar';
+$labels['addattachment'] = 'Anexar';
+$labels['charset'] = 'Charset';
+
+$labels['attachments'] = 'Anexos';
+$labels['upload'] = 'Upload';
+$labels['close'] = 'Fechar';
+
+$labels['low'] = 'Mais baixo';
+$labels['lowest'] = 'Baixo';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'Alta';
+$labels['highest'] = 'Mais alta';
+
+$labels['nosubject'] = '(no assunto)';
+$labels['showimages'] = 'Exibir imagens';
+
+
+// address boook
+$labels['name'] = 'Nome completo';
+$labels['firstname'] = 'Primeiro nome';
+$labels['surname'] = 'Sobrenome';
+$labels['email'] = 'E-Mail';
+
+$labels['addcontact'] = 'Incluir novo contato';
+$labels['editcontact'] = 'Editar contato';
+
+$labels['edit'] = 'Editar';
+$labels['cancel'] = 'Cancelar';
+$labels['save'] = 'Salvar';
+$labels['delete'] = 'Apagar';
+
+$labels['newcontact'] = 'Criar novo contato';
+$labels['deletecontact'] = 'Apagar contatos selecionados';
+$labels['composeto'] = 'Escrever mensagem para';
+$labels['contactsfromto'] = 'Contatos $from - $to of $count';
+$labels['print'] = 'Imprimir';
+$labels['export'] = 'Exportar';
+
+// LDAP search
+$labels['ldapsearch'] = 'Pesquisa no diretório LDAP';
+
+$labels['ldappublicsearchname'] = 'Nome do contado';
+$labels['ldappublicsearchtype'] = 'Pesquisa exata?';
+$labels['ldappublicserverselect'] = 'Selecionar servidores';
+$labels['ldappublicsearchfield'] = 'Pesquisar em';
+$labels['ldappublicsearchform'] = 'Procurar por um contato';
+$labels['ldappublicsearch'] = 'Pesquisar';
+
+
+// settings
+$labels['settingsfor'] = 'Configurações para';
+
+$labels['preferences'] = 'Preferências';
+$labels['userpreferences'] = 'Preferências do usuário';
+$labels['editpreferences'] = 'Editar preferências do usuário';
+
+$labels['identities'] = 'Identidades';
+$labels['manageidentities'] = 'Gerenciar identidades para essa conta';
+$labels['newidentity'] = 'Nova identidade';
+
+$labels['newitem'] = 'Novo item';
+$labels['edititem'] = 'Editar item';
+
+$labels['setdefault'] = 'Padrão';
+$labels['language'] = 'Idioma';
+$labels['timezone'] = 'Time zone';
+$labels['pagesize'] = 'Linhas por página';
+$labels['signature'] = 'Assinatura';
+
+$labels['folder'] = 'Pasta';
+$labels['folders'] = 'Pastas';
+$labels['foldername'] = 'Nome da pasta';
+$labels['subscribed'] = 'Assinado';
+$labels['create'] = 'Criar';
+$labels['createfolder'] = 'Criar nova pasta';
+$labels['deletefolder'] = 'Apagar pasta';
+$labels['managefolders'] = 'Gerenciar pastas';
+
+$labels['sortby'] = 'Ordenado por';
+$labels['sortasc'] = 'Ascendente';
+$labels['sortdesc'] = 'Descendente';
+
+// New labels since 0.1beta
+
+$labels['welcome'] = 'Bem-vindo ao $product';
+
+$labels['unknown'] = 'Desconhecido';
+$labels['unlimited'] = 'Ilimitado';
+$labels['dstactive'] = 'Horário de verão';
+
+$labels['previouspage'] = 'Exibir página anterior';
+$labels['nextpage'] = 'Exibir próxima página';
+
+$labels['returnreceipt'] = 'Confirmação de leitura';
+
+$labels['checkmail'] = 'Verificando novas mensagens';
+$labels['checkspelling'] = 'Verificando ortografia';
+$labels['resumeediting'] = 'Continuar a edição';
+$labels['revertto'] = 'Reverter para';
+
+$labels['savemessage'] = 'Salvar rascunho';
+$labels['rename'] = 'Renomear';
+$labels['renamefolder'] = 'Renomear pasta';
+
+// I use an additional description field - this might be used somewhere else
+$labels['description'] = 'Descrição';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/pt_BR/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Anderson S. Ferreira <anderson@cnpm.embrapa.br> |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 296 2006-08-06 15:53:41Z thomasb $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Falha no login';
+
+$messages['cookiesdisabled'] = 'Seu navegador não suporta cookies';
+
+$messages['sessionerror'] = 'A sessão do seu navegador é inválida ou expirou';
+
+$messages['imaperror'] = 'A conexão com o servidor IMAP falhou';
+
+$messages['nomessagesfound'] = 'Nenhuma mensagem foi encontrada na caixa postal';
+
+$messages['loggedout'] = 'Sua sessão foi finalizada com sucesso. Até breve!';
+
+$messages['mailboxempty'] = 'A caixa postal está vazia';
+
+$messages['loadingdata'] = 'Carregando informações...';
+
+$messages['messagesent'] = 'Mensagem enviada';
+
+$messages['successfullysaved'] = 'Salvo com sucesso';
+
+$messages['addedsuccessfully'] = 'Contato incluído com sucesso';
+
+$messages['contactexists'] = 'Um contato com esse e-mail já existe';
+
+$messages['blockedimages'] = 'Para proteger sua privacidade, as imagens desta mensagem foram bloqueadas';
+
+$messages['encryptedmessage'] = 'Esta mensagem está criptografada e não pode ser exibida. Desculpe.';
+
+$messages['nocontactsfound'] = 'Nenhum contato encontrado';
+
+$messages['sendingfailed'] = 'Falha no envio da mensagem';
+
+$messages['errorsaving'] = 'Um erro ocorreu durante o salvamento';
+
+$messages['errormoving'] = 'Não foi possível mover a mensagem';
+
+$messages['errordeleting'] = 'Não foi possível apagar a mensagem';
+
+$messages['deletecontactconfirm'] = 'Deseja realmente excluir os contatos selecionados?';
+
+$messages['deletefolderconfirm'] = 'Deseja realmente excluir esta pasta?';
+
+$messages['purgefolderconfirm'] = 'Deseja realmente excluir todas mensagens desta pasta';
+
+$messages['formincomplete'] = 'Os campos não foram completamente preenchidos';
+
+$messages['noemailwarning'] = 'Por favor, informe um endereço de e-mail válido';
+
+$messages['nonamewarning'] = 'Por favor, informe o nome';
+
+$messages['nopagesizewarning'] = 'Por favor, informe o tamanho da página';
+
+$messages['norecipientwarning'] = 'Por favor, informe pelo menos um destinatário';
+
+$messages['nosubjectwarning'] = 'O campo assunto não foi informado. Deseja incluí-lo agora?';
+
+$messages['nobodywarning'] = 'Enviar a mensagem se texto?';
+
+$messages['notsentwarning'] = 'A mensegem não foi enviada, deseja excluí-la?';
+
+$messages['noldapserver'] = 'Por favor, selecione um servidor LDAP para a pesquisa';
+
+$messages['nocontactsreturned'] = 'Nenhum contato foi encontrado';
+
+$messages['nosearchname'] = 'Por favor, informe o nome do contado ou seu endereço de e-mail';
+
+// New messages since 0.1beta
+$messages['checking'] = 'Verificando...';
+
+$messages['nospellerrors'] = 'Nenhum erro ortográfico foi encontrado';
+
+$messages['checkingmail'] = 'Verificando novas mensagens...';
+
+$messages['savingmessage'] = 'Salvando a mensagem...';
+
+$messages['messagesaved'] = 'Mensagem salva como rascunho';
+
+$messages['folderdeleted'] = 'Pasta excluida com sucesso';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/pt_PT/labels.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ +-----------------------------------------------------------------------+
+ | Author: Pedro M. Gouveia <pbodymind@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: labels.inc 267 2006-06-27 21:56:44Z richs $
+
+*/
+
+$labels = array();
+
+// Página de login
+$labels['username'] = 'Utilizador';
+$labels['password'] = 'Senha';
+$labels['server'] = 'Servidor';
+$labels['login'] = 'Entrar';
+
+// taskbar
+$labels['logout'] = 'Sair';
+$labels['mail'] = 'E-mail';
+$labels['settings'] = 'Configurações pessoais';
+$labels['addressbook'] = 'Catálogo de endereços';
+
+// Nome das pastas de correio
+$labels['inbox'] = 'Caixa de entrada';
+$labels['drafts'] = 'Esboços';
+$labels['sent'] = 'Enviados';
+$labels['trash'] = 'Lixeira';
+$labels['drafts'] = 'Rascunhos';
+$labels['junk'] = 'Spam';
+
+// message listing
+$labels['subject'] = 'Assunto';
+$labels['from'] = 'Remetente';
+$labels['to'] = 'Para';
+$labels['cc'] = 'Cc';
+$labels['bcc'] = 'Bcc';
+$labels['replyto'] = 'Responder para';
+$labels['date'] = 'Data';
+$labels['size'] = 'Tamanho';
+$labels['priority'] = 'Prioridade';
+$labels['organization'] = 'Organização';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Pastas';
+$labels['messagesfromto'] = 'Mensagens de $from para $to $count';
+$labels['messagenrof'] = 'Mensagem $nr de $count';
+
+$labels['moveto'] = 'mover para...';
+$labels['download'] = 'download';
+
+$labels['filename'] = 'Arquivo';
+$labels['filesize'] = 'Tamanho';
+
+$labels['preferhtml'] = 'Prefere HTML';
+$labels['htmlmessage'] = 'Mensagem HTML';
+$labels['prettydate'] = 'Formatar datas';
+
+$labels['addtoaddressbook'] = 'Incluir no catálogo de endereços';
+
+// Dias da semana abreviados
+$labels['sun'] = 'Dom';
+$labels['mon'] = 'Seg';
+$labels['tue'] = 'Ter';
+$labels['wed'] = 'Qua';
+$labels['thu'] = 'Qui';
+$labels['fri'] = 'Sex';
+$labels['sat'] = 'Sáb';
+
+// Dias da semana completos
+$labels['sunday'] = 'Domingo';
+$labels['monday'] = 'Segunda-feira';
+$labels['tuesday'] = 'Terça-feira';
+$labels['wednesday'] = 'Quarta-feira';
+$labels['thursday'] = 'Quinta-feira';
+$labels['friday'] = 'Sexta-feira';
+$labels['saturday'] = 'Sábado';
+
+$labels['today'] = 'Hoje';
+
+// toolbar buttons
+$labels['checkmail'] = 'Verificar para ver se há mensagens novas';
+$labels['writenewmessage'] = 'Criar nova mensagem';
+$labels['replytomessage'] = 'Responder';
+$labels['replytoallmessage'] = 'Responder para todos';
+$labels['forwardmessage'] = 'Encaminhar';
+$labels['deletemessage'] = 'Mover para lixeira';
+$labels['printmessage'] = 'Imprimir';
+$labels['previousmessages'] = 'Anterior';
+$labels['nextmessages'] = 'Próximo';
+$labels['backtolist'] = 'Voltar';
+$labels['viewsource'] = 'Exibir código fonte';
+
+$labels['select'] = 'Selecionar';
+$labels['all'] = 'Todas';
+$labels['none'] = 'Nenhuma';
+$labels['unread'] = 'Não lidas';
+
+$labels['compact'] = 'Compactar';
+$labels['empty'] = 'Vazio';
+$labels['purge'] = 'Apagar';
+
+$labels['quota'] = 'Quota de espaço';
+
+
+// message compose
+$labels['compose'] = 'Escrever mensagem';
+$labels['savemessage'] = 'Excepto este esboço';
+$labels['sendmessage'] = 'Enviar';
+$labels['addattachment'] = 'Anexar';
+$labels['charset'] = 'Charset';
+
+$labels['attachments'] = 'Anexos';
+$labels['upload'] = 'Upload';
+$labels['close'] = 'Fechar';
+
+$labels['low'] = 'Baixo';
+$labels['lowest'] = 'Muito Baixo';
+$labels['normal'] = 'Normal';
+$labels['high'] = 'Alta';
+$labels['highest'] = 'Muito alta';
+
+$labels['nosubject'] = '(sem assunto)';
+$labels['showimages'] = 'Exibir imagens';
+
+
+// address boook
+$labels['name'] = 'Nome completo';
+$labels['firstname'] = 'Primeiro nome';
+$labels['surname'] = 'Apelido';
+$labels['email'] = 'E-Mail';
+
+$labels['addcontact'] = 'Incluir novo contacto';
+$labels['editcontact'] = 'Editar contacto';
+
+$labels['edit'] = 'Editar';
+$labels['cancel'] = 'Cancelar';
+$labels['save'] = 'Salvar';
+$labels['delete'] = 'Apagar';
+
+$labels['newcontact'] = 'Criar novo contacto';
+$labels['addcontact'] = 'Adicionar contacto seleccionado ao catálogo de endereços';
+$labels['deletecontact'] = 'Apagar contactos seleccionados';
+$labels['composeto'] = 'Escrever mensagem para';
+$labels['contactsfromto'] = 'Contactos $from - $to de $count';
+$labels['print'] = 'Imprimir';
+$labels['export'] = 'Exportar';
+
+// LDAP search
+$labels['ldapsearch'] = 'Pesquisa no diretório LDAP';
+
+$labels['ldappublicsearchname'] = 'Nome do contacto';
+$labels['ldappublicsearchtype'] = 'Pesquisa certa?';
+$labels['ldappublicserverselect'] = 'Seleccionar servidores';
+$labels['ldappublicsearchfield'] = 'Pesquisar em';
+$labels['ldappublicsearchform'] = 'Procurar por contacto';
+$labels['ldappublicsearch'] = 'Pesquisar';
+
+
+// settings
+$labels['settingsfor'] = 'Configurações para';
+
+$labels['preferences'] = 'Preferências';
+$labels['userpreferences'] = 'Preferências do tilizador';
+$labels['editpreferences'] = 'Editar preferências do utilizador';
+
+$labels['identities'] = 'Identidades';
+$labels['manageidentities'] = 'Gerenciar identidades para a sua conta';
+$labels['newidentity'] = 'Nova identidade';
+
+$labels['newitem'] = 'Novo item';
+$labels['edititem'] = 'Editar item';
+
+$labels['setdefault'] = 'Padrão';
+$labels['language'] = 'Idioma';
+$labels['timezone'] = 'Hora Local';
+$labels['pagesize'] = 'Linhas por página';
+$labels['signature'] = 'Assinatura';
+
+$labels['folder'] = 'Pasta';
+$labels['folders'] = 'Pastas';
+$labels['foldername'] = 'Nome da pasta';
+$labels['subscribed'] = 'Assinado';
+$labels['create'] = 'Criar';
+$labels['createfolder'] = 'Criar nova pasta';
+$labels['deletefolder'] = 'Apagar pasta';
+$labels['managefolders'] = 'Gerenciar pastas';
+
+$labels['sortby'] = 'Ordenado por';
+$labels['sortasc'] = 'Ascendente';
+$labels['sortdesc'] = 'Descendente';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/pt_PT/messages.inc |
+ | |
+ | Language file of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ +-----------------------------------------------------------------------+
+ | Author: Pedro M. Gouveia <pbodymind@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: messages.inc 267 2006-06-27 21:56:44Z richs $
+
+*/
+
+$messages = array();
+
+$messages['loginfailed'] = 'Falha no login';
+
+$messages['cookiesdisabled'] = 'O seu navegador não suporta cookies';
+
+$messages['sessionerror'] = 'A sua sessão é inválida ou expirou';
+
+$messages['imaperror'] = 'O servidor IMAP falhou';
+
+$messages['nomessagesfound'] = 'Nenhuma mensagem foi encontrada na caixa postal';
+
+$messages['loggedout'] = 'Sua sessão foi finalizada com sucesso. Até breve!';
+
+$messages['mailboxempty'] = 'A caixa postal está vazia';
+
+$messages['loading'] = 'Carregando...';
+
+$messages['loadingdata'] = 'Carregando Informações...';
+
+$messages['checkingmail'] = 'Verificar para ver se há mensagens novas...';
+
+$messages['sendingmessage'] = 'A enviar mensagem...';
+
+$messages['messagesent'] = 'Mensagem enviada';
+
+$messages['savingmessage'] = 'Mensagem do Saving...';
+
+$messages['messagesaved'] = 'Mensagem conservada aos esboços';
+
+$messages['successfullysaved'] = 'Ficheiro gravado';
+
+$messages['addedsuccessfully'] = 'Contacto adicionado ao catálogo de endereços';
+
+$messages['contactexists'] = 'Um contacto com esse e-mail já existe';
+
+$messages['blockedimages'] = 'Para proteger sua privacidade, as imagens desta mensagem foram bloqueadas';
+
+$messages['encryptedmessage'] = 'Esta mensagem está criptografada e não pode ser exibida. Desculpe.';
+
+$messages['nocontactsfound'] = 'Nenhum contacto encontrado';
+
+$messages['sendingfailed'] = 'Falha no envio da mensagem';
+
+$messages['errorsaving'] = 'Ocorreu um erro a gravar o ficheiro';
+
+$messages['errormoving'] = 'Não foi possível mover a mensagem';
+
+$messages['errordeleting'] = 'Não foi possível apagar a mensagem';
+
+$messages['deletecontactconfirm'] = 'Deseja realmente excluir os contatos seleccionados?';
+
+$messages['deletefolderconfirm'] = 'Deseja realmente excluir esta pasta?';
+
+$messages['purgefolderconfirm'] = 'Deseja realmente excluir todas mensagens desta pasta';
+
+$messages['formincomplete'] = 'Alguns campos não foram preenchidos';
+
+$messages['noemailwarning'] = 'Por favor, insira um endereço de e-mail válido';
+
+$messages['nonamewarning'] = 'Por favor, insira o nome';
+
+$messages['nopagesizewarning'] = 'Por favor, insira o tamanho da página';
+
+$messages['norecipientwarning'] = 'Por favor, informe pelo menos um destinatário';
+
+$messages['nosubjectwarning'] = 'O campo assunto não foi preenchido. Deseja preenche-lo agora?';
+
+$messages['nobodywarning'] = 'Enviar a mensagem sem texto?';
+
+$messages['notsentwarning'] = 'A mensagem não foi enviada, deseja excluí-la?';
+
+$messages['noldapserver'] = 'Por favor, selecione um servidor LDAP para a pesquisa';
+
+$messages['nocontactsreturned'] = 'Nenhum contacto foi encontrado';
+
+$messages['nosearchname'] = 'Por favor, informe o nome do contacto ou o seu endereço de e-mail';
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/delete.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Delete the submitted contacts (CIDs) from the users address book |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: delete.inc 429 2006-12-22 22:26:24Z thomasb $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+if ($_GET['_cid'] && preg_match('/^[0-9]+(,[0-9]+)*$/',$_GET['_cid']))
+ {
+ $DB->query("UPDATE ".get_table_name('contacts')."
+ SET del=1
+ WHERE user_id=?
+ AND contact_id IN (".$_GET['_cid'].")",
+ $_SESSION['user_id']);
+
+ $count = $DB->affected_rows();
+ if (!$count)
+ {
+ // send error message
+ exit;
+ }
+
+
+ // count contacts for this user
+ $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
+ FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id=?",
+ $_SESSION['user_id']);
+
+ $sql_arr = $DB->fetch_assoc($sql_result);
+ $rowcount = $sql_arr['rows'];
+
+ // update message count display
+ $pages = ceil($rowcount/$CONFIG['pagesize']);
+ $commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
+ $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+
+
+ // add new rows from next page (if any)
+ if ($_GET['_from']!='show' && $pages>1 && $_SESSION['page'] < $pages)
+ {
+ $start_row = ($_SESSION['page'] * $CONFIG['pagesize']) - $count;
+
+ // get contacts from DB
+ $sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id=?
+ ORDER BY name",
+ $start_row,
+ $count,
+ $_SESSION['user_id']);
+
+ $commands .= rcmail_js_contacts_list($sql_result);
+
+/*
+ // define list of cols to be displayed
+ $a_show_cols = array('name', 'email');
+
+ while ($sql_arr = $DB->fetch_assoc($sql_result))
+ {
+ $a_row_cols = array();
+
+ // format each col
+ foreach ($a_show_cols as $col)
+ {
+ $cont = rep_specialchars_output($sql_arr[$col]);
+ $a_row_cols[$col] = $cont;
+ }
+
+ $commands .= sprintf("this.add_contact_row(%s, %s);\n",
+ $sql_arr['contact_id'],
+ array2js($a_row_cols));
+ }
+*/
+ }
+
+ // send response
+ rcube_remote_response($commands);
+ }
+
+exit;
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/edit.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Show edit form for a contact entry or to add a new one |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: edit.inc 88 2005-12-03 16:54:12Z roundcube $
+
+*/
+
+
+if (($_GET['_cid'] || $_POST['_cid']) && $_action=='edit')
+ {
+ $cid = $_POST['_cid'] ? $_POST['_cid'] : $_GET['_cid'];
+ $DB->query("SELECT * FROM ".get_table_name('contacts')."
+ WHERE contact_id=?
+ AND user_id=?
+ AND del<>1",
+ $cid,
+ $_SESSION['user_id']);
+
+ $CONTACT_RECORD = $DB->fetch_assoc();
+
+ if (is_array($CONTACT_RECORD))
+ $OUTPUT->add_script(sprintf("%s.set_env('cid', '%s');", $JS_OBJECT_NAME, $CONTACT_RECORD['contact_id']));
+ }
+
+
+
+function rcmail_contact_editform($attrib)
+ {
+ global $CONTACT_RECORD, $JS_OBJECT_NAME;
+
+ if (!$CONTACT_RECORD && $GLOBALS['_action']!='add')
+ return rcube_label('contactnotfound');
+
+ // add some labels to client
+ rcube_add_label('noemailwarning');
+ rcube_add_label('nonamewarning');
+
+ list($form_start, $form_end) = get_form_tags($attrib);
+ unset($attrib['form']);
+
+
+ // a specific part is requested
+ if ($attrib['part'])
+ {
+ $out = $form_start;
+ $out .= rcmail_get_edit_field($attrib['part'], $CONTACT_RECORD[$attrib['part']], $attrib);
+ return $out;
+ }
+
+
+ // return the complete address edit form as table
+ $out = "$form_start<table>\n\n";
+
+ $a_show_cols = array('name', 'firstname', 'surname', 'email');
+ foreach ($a_show_cols as $col)
+ {
+ $attrib['id'] = 'rcmfd_'.$col;
+ $title = rcube_label($col);
+ $value = rcmail_get_edit_field($col, $CONTACT_RECORD[$col], $attrib);
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $attrib['id'],
+ $title,
+ $value);
+ }
+
+ $out .= "\n</table>$form_end";
+
+ return $out;
+ }
+
+
+// similar function as in /steps/settings/edit_identity.inc
+function get_form_tags($attrib)
+ {
+ global $CONTACT_RECORD, $OUTPUT, $JS_OBJECT_NAME, $EDIT_FORM, $SESS_HIDDEN_FIELD;
+
+ $form_start = '';
+ if (!strlen($EDIT_FORM))
+ {
+ $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task']));
+ $hiddenfields->add(array('name' => '_action', 'value' => 'save'));
+
+ if ($_GET['_framed'] || $_POST['_framed'])
+ $hiddenfields->add(array('name' => '_framed', 'value' => 1));
+
+ if ($CONTACT_RECORD['contact_id'])
+ $hiddenfields->add(array('name' => '_cid', 'value' => $CONTACT_RECORD['contact_id']));
+
+ $form_start = !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : '';
+ $form_start .= "\n$SESS_HIDDEN_FIELD\n";
+ $form_start .= $hiddenfields->show();
+ }
+
+ $form_end = (strlen($EDIT_FORM) && !strlen($attrib['form'])) ? '</form>' : '';
+ $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form';
+
+ if (!strlen($EDIT_FORM))
+ $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('editform', '$form_name');");
+
+ $EDIT_FORM = $form_name;
+
+ return array($form_start, $form_end);
+ }
+
+
+
+if (!$CONTACT_RECORD && template_exists('addcontact'))
+ parse_template('addcontact');
+
+// this will be executed if no template for addcontact exists
+parse_template('editcontact');
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/func.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide addressbook functionality and GUI objects |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: func.inc 127 2006-01-25 22:56:53Z roundcube $
+
+*/
+
+$CONTACTS_LIST = array();
+
+// set list properties and session vars
+if (strlen($_GET['_page']))
+ {
+ $CONTACTS_LIST['page'] = $_GET['_page'];
+ $_SESSION['page'] = $_GET['_page'];
+ }
+else
+ $CONTACTS_LIST['page'] = $_SESSION['page'] ? $_SESSION['page'] : 1;
+
+// disable the ldap public search button if there's no servers configured
+$enable_ldap = 'true';
+if (!$CONFIG['ldap_public'])
+ $enable_ldap = 'false';
+
+$OUTPUT->add_script("$JS_OBJECT_NAME.set_env('ldappublicsearch', $enable_ldap);");
+
+// return the message list as HTML table
+function rcmail_contacts_list($attrib)
+ {
+ global $DB, $CONFIG, $OUTPUT, $CONTACTS_LIST, $JS_OBJECT_NAME;
+
+ //$skin_path = $CONFIG['skin_path'];
+ //$image_tag = '<img src="%s%s" alt="%s" border="0" />';
+
+ // count contacts for this user
+ $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
+ FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id=?",
+ $_SESSION['user_id']);
+
+ $sql_arr = $DB->fetch_assoc($sql_result);
+ $rowcount = $sql_arr['rows'];
+
+ if ($rowcount)
+ {
+ $start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize'];
+
+ // get contacts from DB
+ $sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id= ?
+ ORDER BY name",
+ $start_row,
+ $CONFIG['pagesize'],
+ $_SESSION['user_id']);
+ }
+ else
+ $sql_result = NULL;
+
+
+ // add id to message list table if not specified
+ if (!strlen($attrib['id']))
+ $attrib['id'] = 'rcmAddressList';
+
+ // define list of cols to be displayed
+ $a_show_cols = array('name', 'email');
+
+ // create XHTML table
+ $out = rcube_table_output($attrib, $sql_result, $a_show_cols, 'contact_id');
+
+ // set client env
+ $javascript = sprintf("%s.gui_object('contactslist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
+ $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $CONTACTS_LIST['page']);
+ $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($rowcount/$CONFIG['pagesize']));
+ $javascript .= "rcmail.set_env('newcontact', '" . rcube_label('newcontact') . "');";
+ //$javascript .= sprintf("%s.set_env('contacts', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr));
+
+ $OUTPUT->add_script($javascript);
+
+ // add some labels to client
+ rcube_add_label('deletecontactconfirm');
+
+ return $out;
+ }
+
+
+
+function rcmail_js_contacts_list($sql_result, $obj_name='this')
+ {
+ global $DB;
+
+ $commands = '';
+
+ if (!$sql_result)
+ return '';
+
+ // define list of cols to be displayed
+ $a_show_cols = array('name', 'email');
+
+ while ($sql_arr = $DB->fetch_assoc($sql_result))
+ {
+ $a_row_cols = array();
+
+ // format each col
+ foreach ($a_show_cols as $col)
+ {
+ $cont = rep_specialchars_output($sql_arr[$col]);
+ $a_row_cols[$col] = $cont;
+ }
+
+ $commands .= sprintf("%s.add_contact_row(%s, %s);\n",
+ $obj_name,
+ $sql_arr['contact_id'],
+ array2js($a_row_cols));
+ }
+
+ return $commands;
+ }
+
+
+// similar function as /steps/settings/identities.inc::rcmail_identity_frame()
+function rcmail_contact_frame($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmcontactframe';
+
+ $attrib['name'] = $attrib['id'];
+
+ $OUTPUT->add_script(sprintf("%s.set_env('contentframe', '%s');", $JS_OBJECT_NAME, $attrib['name']));
+
+ $attrib_str = create_attrib_string($attrib, array('name', 'id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
+ $out = '<iframe'. $attrib_str . '></iframe>';
+
+ return $out;
+ }
+
+
+function rcmail_rowcount_display($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmcountdisplay';
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('countdisplay', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+
+ // allow the following attributes to be added to the <span> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+
+
+ $out = '<span' . $attrib_str . '>';
+ $out .= rcmail_get_rowcount_text();
+ $out .= '</span>';
+ return $out;
+ }
+
+
+
+function rcmail_get_rowcount_text($max=NULL)
+ {
+ global $CONTACTS_LIST, $CONFIG, $DB;
+
+ $start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize'] + 1;
+
+ // get nr of contacts
+ if ($max===NULL)
+ {
+ $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id=?",
+ $_SESSION['user_id']);
+
+ $max = $DB->num_rows($sql_result);
+ }
+
+ if ($max==0)
+ $out = rcube_label('nocontactsfound');
+ else
+ $out = rcube_label(array('name' => 'contactsfromto',
+ 'vars' => array('from' => $start_row,
+ 'to' => min($max, $start_row + $CONFIG['pagesize'] - 1),
+ 'count' => $max)));
+
+ return $out;
+ }
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/ldapsearch.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Show an ldap search form in the addressbook |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Justin Randell <justin.randell@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: ldapsearchform.inc 159 2006-03-03 16:34:35Z roundcube $
+
+*/
+require_once 'include/rcube_ldap.inc';
+
+/**
+ * draw the ldap public search form
+ */
+function rcmail_ldap_public_search_form($attrib)
+ {
+ global $CONFIG, $JS_OBJECT_NAME, $OUTPUT;
+ if (!isset($CONFIG['ldap_public']))
+ {
+ // no ldap servers to search
+ show_message('noldapserver', 'warning');
+ rcmail_overwrite_action('add');
+ return false;
+ }
+ else
+ {
+ // store some information in the session
+ $_SESSION['ldap_public']['server_count'] = $server_count = count($CONFIG['ldap_public']);
+ $_SESSION['ldap_public']['server_names'] = $server_names = array_keys($CONFIG['ldap_public']);
+ }
+
+ list($form_start, $form_end) = get_form_tags($attrib);
+ $out = "$form_start<table id=\"ldap_public_search_table\">\n\n";
+
+ // search name field
+ $search_name = new textfield(array('name' => '_ldap_public_search_name',
+ 'id' => 'rcmfd_ldap_public_search_name'));
+ $out .= "<tr><td class=\"title\"><label for=\"rcmfd_ldap_public_search_name\">" .
+ rep_specialchars_output(rcube_label('ldappublicsearchname')) .
+ "</label></td><td>" . $search_name->show() . "</td></tr>\n";
+
+
+ // there's more than one server to search for, show a dropdown menu
+ if ($server_count > 1)
+ {
+ $select_server = new select(array('name' => '_ldap_public_servers',
+ 'id' => 'rcfmd_ldap_public_servers'));
+
+ $select_server->add($server_names, $server_names);
+
+ $out .= '<tr><td class="title"><label for="rcfmd_ldap_public_servers">' .
+ rep_specialchars_output(rcube_label('ldappublicserverselect')) .
+ "</label></td><td>" . $select_server->show() . "</td></tr>\n";
+ }
+
+ // foreach configured ldap server, set up the search fields
+ for ($i = 0; $i < $server_count; $i++)
+ {
+ $server = $CONFIG['ldap_public'][$server_names[$i]];
+
+ // only display one search fields select - js takes care of the rest
+ if (!$i)
+ {
+ $field_name = '_ldap_public_search_field';
+ $field_id = 'rcfmd_ldap_public_search_field';
+
+ $search_fields = new select(array('name' => $field_name,
+ 'id' => $field_id));
+
+ $search_fields->add(array_keys($server['search_fields']), array_values($server['search_fields']));
+ $out .= '<tr><td class="title"><label for="' . $field_id . '">' .
+ rep_specialchars_output(rcube_label('ldappublicsearchfield')) .
+ "</label></td><td>" . $search_fields->show() . "</td></tr>\n";
+
+ $attributes = array('name' => '_ldap_public_search_type',
+ 'id' => 'rcmfd_ldap_public_search_type');
+
+ // if there's only one server, and it doesn't accept fuzzy searches,
+ // then check and disable the check box - thanks pieter
+ if ($server_count == 1 && !$server['fuzzy_search'])
+ {
+ $attributes['CHECKED'] = 'CHECKED';
+ $attributes['disabled'] = 'disabled';
+ }
+
+ $search_type = new checkbox($attributes);
+
+ $out .= '<tr id="ldap_fuzzy_search"><td class="title"><label for="rcmfd_ldap_public_search_type">' .
+ rep_specialchars_output(rcube_label('ldappublicsearchtype')) .
+ "</label></td><td>" . $search_type->show() . "</td></tr>\n";
+ }
+
+ if ($server_count > 1)
+ {
+ // store the search fields in a js array for each server
+ $js = '';
+ foreach ($server['search_fields'] as $search_name => $search_value)
+ $js .= "['$search_name', '$search_value'], ";
+
+ // store whether this server accepts fuzzy search as last item in array
+ $js .= $server['fuzzy_search'] ? "'fuzzy'" : "'exact'";
+ $OUTPUT->add_script("rcmail.set_env('{$server_names[$i]}_search_fields', new Array($js));");
+ }
+ }
+
+ // add contact button label text
+ $OUTPUT->add_script("rcmail.set_env('addcontact', '" . rcube_label('addcontact') . "');");
+
+ $out .= "\n</table>$form_end";
+ return $out;
+ }
+
+/**
+ * get search values and return ldap contacts
+ */
+function rcmail_ldap_public_list()
+ {
+ // just return if we are not being called from a search form
+ if (!isset($_POST['_action']))
+ return null;
+
+ global $CONFIG, $OUTPUT, $JS_OBJECT_NAME;
+
+ // show no search name warning and exit
+ if (empty($_POST['_ldap_public_search_name']) || trim($_POST['_ldap_public_search_name']) == '')
+ {
+ show_message('nosearchname', 'warning');
+ return false;
+ }
+
+ // set up ldap server(s) array or bail
+ if ($_SESSION['ldap_public']['server_count'] > 1)
+ // show no ldap server warning and exit
+ if (empty($_POST['_ldap_public_servers']))
+ {
+ show_message('noldappublicserver', 'warning');
+ return false;
+ }
+ else
+ $server_name = $_POST['_ldap_public_servers'];
+ else if ($_SESSION['ldap_public']['server_count'] == 1)
+ $server_name = $_SESSION['ldap_public']['server_names'][0];
+ else
+ return false;
+
+ // get search parameters
+ $search_value = $_POST['_ldap_public_search_name'];
+ $search_field = $_POST['_ldap_public_search_field'];
+
+ // only use the post var for search type if the ldap server allows 'like'
+ $exact = true;
+ if ($CONFIG['ldap_public'][$server_name]['fuzzy_search'])
+ $exact = isset($_POST['_ldap_public_search_type']) ? true : false;
+
+ // perform an ldap search
+ $contacts = rcmail_ldap_contact_search($search_value,
+ $search_field,
+ $CONFIG['ldap_public'][$server_name],
+ $exact);
+
+ // if no results, show a warning and return
+ if (!$contacts)
+ {
+ show_message('nocontactsreturned', 'warning');
+ return false;
+ }
+
+ // add id to message list table if not specified
+ if (!strlen($attrib['id']))
+ $attrib['id'] = 'ldapAddressList';
+
+ // define table class
+ $attrib['class'] = 'records-table';
+ $attrib['cellspacing'] = 0;
+
+ // define list of cols to be displayed
+ $a_show_cols = array('name', 'email');
+
+ // create XHTML table
+ $out = rcube_table_output($attrib, $contacts, $a_show_cols, 'row_id');
+
+ // set client env
+ $javascript = "$JS_OBJECT_NAME.gui_object('ldapcontactslist', '{$attrib['id']}');\n";
+ $OUTPUT->add_script($javascript);
+
+ return $out;
+ }
+
+/**
+ * perform search for contacts from given public ldap server
+ */
+function rcmail_ldap_contact_search($search_value, $search_field, $server, $exact=true)
+ {
+ global $CONFIG;
+
+ $attributes = array($server['name_field'], $server['mail_field']);
+
+ $LDAP = new rcube_ldap();
+ if ($LDAP->connect($server['hosts'], $server['port'], $server['protocol']))
+ {
+ $filter = "$search_field=" . ($exact ? $search_value : "*$search_value*");
+ $result = $LDAP->search($server['base_dn'],
+ $filter,
+ $attributes,
+ $server['scope'],
+ $sort=null);
+
+ // add any results to contact array
+ if ($result['count'])
+ {
+ for ($n = 0; $n < $result['count']; $n++)
+ {
+ $contacts[$n]['name'] = $result[$n][$server['name_field']][0];
+ $contacts[$n]['email'] = $result[$n][$server['mail_field']][0];
+ $contacts[$n]['row_id'] = $n + 1;
+ }
+ }
+ }
+ else
+ return false;
+
+ // cleanup
+ $LDAP->close();
+
+ if (!$result['count'])
+ return false;
+
+ // weed out duplicate emails
+ for ($n = 0; $n < $result['count']; $n++)
+ for ($i = 0; $i < $result['count']; $i++)
+ if ($contacts[$i]['email'] == $contacts[$n]['email'] && $i != $n)
+ unset($contacts[$n]);
+
+ return $contacts;
+ }
+
+function get_form_tags($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME, $EDIT_FORM, $SESS_HIDDEN_FIELD;
+
+ $form_start = '';
+ if (!strlen($EDIT_FORM))
+ {
+ $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task']));
+ $hiddenfields->add(array('name' => '_action', 'value' => 'ldappublicsearch'));
+
+ if ($_framed)
+ $hiddenfields->add(array('name' => '_framed', 'value' => 1));
+
+ $form_start .= !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : '';
+ $form_start .= "\n$SESS_HIDDEN_FIELD\n";
+ $form_start .= $hiddenfields->show();
+ }
+
+ $form_end = (strlen($EDIT_FORM) && !strlen($attrib['form'])) ? '</form>' : '';
+ $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form';
+
+ $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('ldappublicsearchform', '$form_name');");
+
+ $EDIT_FORM = $form_name;
+
+ return array($form_start, $form_end);
+ }
+
+parse_template('ldappublicsearch');
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/list.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Send contacts list to client (as remote response) |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: list.inc 88 2005-12-03 16:54:12Z roundcube $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+// count contacts for this user
+$sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
+ FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id=?",
+ $_SESSION['user_id']);
+
+$sql_arr = $DB->fetch_assoc($sql_result);
+$rowcount = $sql_arr['rows'];
+
+// update message count display
+$pages = ceil($rowcount/$CONFIG['pagesize']);
+$commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
+$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+
+$start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize'];
+
+// get contacts from DB
+$sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
+ WHERE del<>1
+ AND user_id=?
+ ORDER BY name",
+ $start_row,
+ $CONFIG['pagesize'],
+ $_SESSION['user_id']);
+
+$commands .= rcmail_js_contacts_list($sql_result);
+
+// send response
+rcube_remote_response($commands);
+
+exit;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/save.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Save a contact entry or to add a new one |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: save.inc 159 2006-03-03 16:34:35Z roundcube $
+
+*/
+
+// check input
+if ((empty($_POST['_name']) || empty($_POST['_email'])) && empty($_GET['_framed']))
+ {
+ show_message('formincomplete', 'warning');
+ rcmail_overwrite_action(empty($_POST['_cid']) ? 'add' : 'show');
+ return;
+ }
+
+// setup some vars we need
+$a_save_cols = array('name', 'firstname', 'surname', 'email');
+$contacts_table = get_table_name('contacts');
+
+// update an existing contact
+if (!empty($_POST['_cid']))
+ {
+ $a_write_sql = array();
+
+ foreach ($a_save_cols as $col)
+ {
+ $fname = '_'.$col;
+ if (!isset($_POST[$fname]))
+ continue;
+
+ $a_write_sql[] = sprintf("%s=%s",
+ $DB->quoteIdentifier($col),
+ $DB->quote(get_input_value($fname, RCUBE_INPUT_POST)));
+ }
+
+ if (sizeof($a_write_sql))
+ {
+ $DB->query("UPDATE $contacts_table
+ SET changed=now(), ".join(', ', $a_write_sql)."
+ WHERE contact_id=?
+ AND user_id=?
+ AND del<>1",
+ $_POST['_cid'],
+ $_SESSION['user_id']);
+
+ $updated = $DB->affected_rows();
+ }
+
+ if ($updated)
+ {
+ $_action = 'show';
+ show_message('successfullysaved', 'confirmation');
+
+ if ($_framed)
+ {
+ // define list of cols to be displayed
+ $a_show_cols = array('name', 'email');
+ $a_js_cols = array();
+
+ $sql_result = $DB->query("SELECT * FROM $contacts_table
+ WHERE contact_id=?
+ AND user_id=?
+ AND del<>1",
+ $_POST['_cid'],
+ $_SESSION['user_id']);
+
+ $sql_arr = $DB->fetch_assoc($sql_result);
+ foreach ($a_show_cols as $col)
+ $a_js_cols[] = (string)$sql_arr[$col];
+
+ // update the changed col in list
+ $OUTPUT->add_script(sprintf("if(parent.%s)parent.%s.update_contact_row('%d', %s);",
+ $JS_OBJECT_NAME,
+ $JS_OBJECT_NAME,
+ $_POST['_cid'],
+ array2js($a_js_cols)));
+
+ // show confirmation
+ show_message('successfullysaved', 'confirmation');
+ }
+ }
+ else
+ {
+ // show error message
+ show_message('errorsaving', 'error');
+ rcmail_overwrite_action('show');
+ }
+ }
+
+// insert a new contact
+else
+ {
+ $a_insert_cols = $a_insert_values = array();
+
+ // check for existing contacts
+ $sql = "SELECT 1 FROM $contacts_table
+ WHERE user_id = {$_SESSION['user_id']}
+ AND del <> '1' ";
+
+ // get email and name, build sql for existing user check
+ if (isset($_GET['_emails']) && isset($_GET['_names']))
+ {
+ $sql .= "AND email IN (";
+ $emails = explode(',', get_input_value('_emails', RCUBE_INPUT_GET));
+ $names = explode(',', get_input_value('_names', RCUBE_INPUT_GET));
+ $count = count($emails);
+ $n = 0;
+ foreach ($emails as $email)
+ {
+ $end = (++$n == $count) ? '' : ',';
+ $sql .= $DB->quote($email) . $end;
+ }
+ $sql .= ")";
+ $ldap_form = true;
+ }
+ else if (isset($_POST['_email']))
+ $sql .= "AND email = " . $DB->quote(get_input_value('_email', RCUBE_INPUT_POST));
+
+ $sql_result = $DB->query($sql);
+
+ // show warning message
+ if ($DB->num_rows($sql_result))
+ {
+ show_message('contactexists', 'warning');
+
+ if ($ldap_form)
+ rcmail_overwrite_action('ldappublicsearch');
+ else
+ rcmail_overwrite_action('add');
+
+ return;
+ }
+
+ if ($ldap_form)
+ {
+ $n = 0;
+ foreach ($emails as $email)
+ {
+ $DB->query("INSERT INTO $contacts_table
+ (user_id, name, email
+ VALUES ({$_SESSION['user_id']}," . $DB->quote($names[$n++]) . "," .
+ $DB->quote($email) . ")");
+ $insert_id[] = $DB->insert_id();
+ }
+ }
+ else
+ {
+ foreach ($a_save_cols as $col)
+ {
+ $fname = '_'.$col;
+ if (!isset($_POST[$fname]))
+ continue;
+
+ $a_insert_cols[] = $col;
+ $a_insert_values[] = $DB->quote(get_input_value($fname, RCUBE_INPUT_POST));
+ }
+
+ if (sizeof($a_insert_cols))
+ {
+ $DB->query("INSERT INTO $contacts_table
+ (user_id, changed, del, ".join(', ', $a_insert_cols).")
+ VALUES (?, now(), 0, ".join(', ', $a_insert_values).")",
+ $_SESSION['user_id']);
+
+ $insert_id = $DB->insert_id(get_sequence_name('contacts'));
+ }
+ }
+
+ if ($insert_id)
+ {
+ if (!$ldap_form)
+ {
+ $_action = 'show';
+ $_GET['_cid'] = $insert_id;
+
+ if ($_framed)
+ {
+ // add contact row or jump to the page where it should appear
+ $commands = sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME);
+ $sql_result = $DB->query("SELECT * FROM $contacts_table
+ WHERE contact_id=?
+ AND user_id=?",
+ $insert_id,
+ $_SESSION['user_id']);
+ $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME);
+
+ $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n",
+ $JS_OBJECT_NAME,
+ $JS_OBJECT_NAME,
+ $insert_id);
+
+ // update record count display
+ $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n",
+ $JS_OBJECT_NAME,
+ $JS_OBJECT_NAME,
+ rcmail_get_rowcount_text());
+
+ $OUTPUT->add_script($commands);
+ }
+
+ // show confirmation
+ show_message('successfullysaved', 'confirmation');
+ }
+ else
+ {
+ // add contact row or jump to the page where it should appear
+ $commands = '';
+ foreach ($insert_id as $id)
+ {
+ $sql_result = $DB->query("SELECT * FROM $contacts_table
+ WHERE contact_id = $id
+ AND user_id = {$_SESSION['user_id']}");
+
+ $commands .= sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME);
+ $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME);
+ $last_id = $id;
+ }
+
+ // display the last insert id
+ $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n",
+ $JS_OBJECT_NAME,
+ $JS_OBJECT_NAME,
+ $last_id);
+
+ // update record count display
+ $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n",
+ $JS_OBJECT_NAME,
+ $JS_OBJECT_NAME,
+ rcmail_get_rowcount_text());
+
+ $OUTPUT->add_script($commands);
+ rcmail_overwrite_action('ldappublicsearch');
+ }
+
+ // show confirmation
+ show_message('successfullysaved', 'confirmation');
+ }
+ else
+ {
+ // show error message
+ show_message('errorsaving', 'error');
+ rcmail_overwrite_action('add');
+ }
+ }
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/show.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Show contact details |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: show.inc 88 2005-12-03 16:54:12Z roundcube $
+
+*/
+
+
+if ($_GET['_cid'] || $_POST['_cid'])
+ {
+ $cid = $_POST['_cid'] ? $_POST['_cid'] : $_GET['_cid'];
+ $DB->query("SELECT * FROM ".get_table_name('contacts')."
+ WHERE contact_id=?
+ AND user_id=?
+ AND del<>1",
+ $cid,
+ $_SESSION['user_id']);
+
+ $CONTACT_RECORD = $DB->fetch_assoc();
+
+ if (is_array($CONTACT_RECORD))
+ $OUTPUT->add_script(sprintf("%s.set_env('cid', '%s');", $JS_OBJECT_NAME, $CONTACT_RECORD['contact_id']));
+ }
+
+
+
+function rcmail_contact_details($attrib)
+ {
+ global $CONTACT_RECORD, $JS_OBJECT_NAME;
+
+ if (!$CONTACT_RECORD)
+ return show_message('contactnotfound');
+
+ // a specific part is requested
+ if ($attrib['part'])
+ return rep_specialchars_output($CONTACT_RECORD[$attrib['part']]);
+
+
+ // return the complete address record as table
+ $out = "<table>\n\n";
+
+ $a_show_cols = array('name', 'firstname', 'surname', 'email');
+ foreach ($a_show_cols as $col)
+ {
+ if ($col=='email' && $CONTACT_RECORD[$col])
+ $value = sprintf('<a href="#compose" onclick="%s.command(\'compose\', %d)" title="%s">%s</a>',
+ $JS_OBJECT_NAME,
+ $CONTACT_RECORD['contact_id'],
+ rcube_label('composeto'),
+ $CONTACT_RECORD[$col]);
+ else
+ $value = rep_specialchars_output($CONTACT_RECORD[$col]);
+
+ $title = rcube_label($col);
+ $out .= sprintf("<tr><td class=\"title\">%s</td><td>%s</td></tr>\n", $title, $value);
+ }
+
+
+ $out .= "\n</table>";
+
+ return $out;
+ }
+
+
+parse_template('showcontact');
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/error.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Display error message page |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: error.inc 50 2005-10-21 12:12:23Z roundcube $
+
+*/
+
+
+// browser is not compatible with this application
+if ($ERROR_CODE==409)
+ {
+ $user_agent = $GLOBALS['HTTP_SERVER_VARS']['HTTP_USER_AGENT'];
+ $__error_title = 'Your browser does not suit the requirements for this application';
+ $__error_text = <<<EOF
+<i>Supported browsers:</i><br />
+» Netscape 7+<br />
+» Microsoft Internet Explorer 6+<br />
+» Mozilla Firefox 1.0+<br />
+» Opera 8.0+<br />
+» Safari 1.2+<br />
+<br />
+» JavaScript enabled<br />
+» Support for XMLHTTPRequest<br />
+
+<p><i>Your configuration:</i><br />
+$user_agent</p>
+EOF;
+ }
+
+// authorization error
+else if ($ERROR_CODE==401)
+ {
+ $__error_title = "AUTHORIZATION FAILED";
+ $__error_text = "Could not verify that you are authorized to access this service!<br />\n".
+ "Please contact your server-administrator.";
+ }
+
+// failed request (wrong step in URL)
+else if ($ERROR_CODE==404)
+ {
+ $__error_title = "REQUEST FAILED/FILE NOT FOUND";
+ $request_url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
+ $__error_text = <<<EOF
+The requested page was not found!<br />
+Please contact your server-administrator.
+
+<p><i>Failed request:</i><br />
+http://$request_url</p>
+EOF;
+ }
+
+
+// system error
+else
+ {
+ $__error_title = "SERVICE CURRENTLY NOT AVAILABLE!";
+ $__error_text = "Please contact your server-administrator.";
+
+ if (($CONFIG['debug_level'] & 4) && $ERROR_MESSAGE)
+ $__error_text = $ERROR_MESSAGE;
+ else
+ $__error_text = 'Error No. '.dechex($ERROR_CODE).')';
+ }
+
+
+// compose page content
+
+$__page_content = <<<EOF
+<div>
+<h3 class="error-title">$__error_title</h3>
+<p class="error-text">$__error_text</p>
+</div>
+EOF;
+
+
+
+if (template_exists('error'))
+ {
+ $OUTPUT->scripts = array();
+ $OUTPUT->script_files = array();
+ parse_template('error');
+ }
+
+
+// print system error page
+print <<<EOF
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><head>
+<title>RoundCube|Mail : ERROR $ERROR_CODE</title>
+<link rel="stylesheet" type="text/css" href="program/style.css" />
+</head>
+<body>
+
+<table border="0" cellsapcing="0" cellpadding="0" width="100%" height="80%"><tr><td align="center">
+
+$__page_content
+
+</td></tr></table>
+
+</body>
+</html>
+EOF;
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/addcontact.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Add the submitted contact to the users address book |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: addcontact.inc 160 2006-03-03 16:36:22Z roundcube $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+if (!empty($_GET['_address']))
+ {
+ $contact_arr = $IMAP->decode_address_list(get_input_value('_address', RCUBE_INPUT_GET, TRUE));
+ if (sizeof($contact_arr))
+ {
+ $contact = $contact_arr[1];
+
+ if ($contact['mailto'])
+ $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('contacts')."
+ WHERE user_id=?
+ AND email=?
+ AND del<>1",
+ $_SESSION['user_id'],$contact['mailto']);
+
+ // contact entry with this mail address exists
+ if ($sql_result && $DB->num_rows($sql_result))
+ $existing_contact = TRUE;
+
+ else if ($contact['mailto'])
+ {
+ $DB->query("INSERT INTO ".get_table_name('contacts')."
+ (user_id, changed, del, name, email)
+ VALUES (?, now(), 0, ?, ?)",
+ $_SESSION['user_id'],
+ $contact['name'],
+ $contact['mailto']);
+
+ $added = $DB->insert_id(get_sequence_name('contacts'));
+ }
+ }
+
+ if ($added)
+ $commands = show_message('addedsuccessfully', 'confirmation');
+ else if ($existing_contact)
+ $commands = show_message('contactexists', 'warning');
+ }
+
+
+if (!$commands)
+ $commands = show_message('errorsavingcontact', 'warning');
+
+rcube_remote_response($commands);
+exit;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/check_recent.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Check for recent messages, in all mailboxes |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: check_recent.inc 233 2006-06-26 17:31:20Z richs $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+$a_mailboxes = $IMAP->list_mailboxes();
+
+foreach ($a_mailboxes as $mbox_name)
+ {
+ if ($mbox_name == $IMAP->get_mailbox_name())
+ {
+ if ($recent_count = $IMAP->messagecount(NULL, 'RECENT', TRUE))
+ {
+ $count = $IMAP->messagecount();
+ $unread_count = $IMAP->messagecount(NULL, 'UNSEEN', TRUE);
+
+ $commands .= sprintf("this.set_unread_count('%s', %d);\n", addslashes($mbox_name), $unread_count);
+ $commands .= sprintf("this.set_env('messagecount', %d);\n", $count);
+ $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text());
+ $commands .= sprintf("this.set_quota('%s');\n", $IMAP->get_quota());
+
+ // add new message headers to list
+ $a_headers = array();
+ for ($i=$recent_count, $id=$count-$recent_count+1; $i>0; $i--, $id++)
+ {
+ $header = $IMAP->get_headers($id, NULL, FALSE);
+ if ($header->recent)
+ $a_headers[] = $header;
+ }
+
+ $commands .= rcmail_js_message_list($a_headers, TRUE);
+ }
+ }
+ else
+ {
+ if ($IMAP->messagecount($mbox_name, 'RECENT'))
+ $commands .= sprintf("this.set_unread_count('%s', %d);\n", addslashes($mbox_name), $IMAP->messagecount($mbox_name, 'UNSEEN'));
+ }
+ }
+
+rcube_remote_response($commands);
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/compose.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Compose a new mail message with all headers and attachments |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: compose.inc 297 2006-08-06 15:55:11Z thomasb $
+
+*/
+
+
+require_once('Mail/mimeDecode.php');
+
+// remove an attachment
+if ($_action=='remove-attachment' && preg_match('/^rcmfile([0-9]+)$/', $_GET['_file'], $regs))
+ {
+ $id = $regs[1];
+ if (is_array($_SESSION['compose']['attachments'][$id]))
+ {
+ @unlink($_SESSION['compose']['attachments'][$id]['path']);
+ $_SESSION['compose']['attachments'][$id] = NULL;
+ $commands = sprintf("parent.%s.remove_from_attachment_list('rcmfile%d');\n", $JS_OBJECT_NAME, $id);
+ rcube_remote_response($commands);
+ exit;
+ }
+ }
+
+
+$MESSAGE_FORM = NULL;
+$REPLY_MESSAGE = NULL;
+$FORWARD_MESSAGE = NULL;
+$DRAFT_MESSAGE = NULL;
+
+// nothing below is called during message composition, only at "new/forward/reply/draft" initialization
+// since there are many ways to leave the compose page improperly, it seems necessary to clean-up an old
+// compose when a "new/forward/reply/draft" is called - otherwise the old session attachments will appear
+
+rcmail_compose_cleanup();
+$_SESSION['compose'] = array('id' => uniqid(rand()));
+
+// add some labels to client
+rcube_add_label('nosubject', 'norecipientwarning', 'nosubjectwarning', 'nobodywarning', 'notsentwarning', 'savingmessage', 'sendingmessage', 'messagesaved');
+
+
+if ($_GET['_reply_uid'] || $_GET['_forward_uid'] || $_GET['_draft_uid'])
+ {
+ $msg_uid = ($_GET['_reply_uid'] ? $_GET['_reply_uid'] : ($_GET['_forward_uid'] ? $_GET['_forward_uid'] : $_GET['_draft_uid']));
+
+ // similar as in program/steps/mail/show.inc
+ $MESSAGE = array();
+ $MESSAGE['headers'] = $IMAP->get_headers($msg_uid);
+
+ $MESSAGE['source'] = rcmail_message_source($msg_uid);
+
+ $mmd = new Mail_mimeDecode($MESSAGE['source']);
+ $MESSAGE['structure'] = $mmd->decode(array('include_bodies' => TRUE,
+ 'decode_headers' => TRUE,
+ 'decode_bodies' => FALSE));
+
+ $MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['headers']->subject);
+ $MESSAGE['parts'] = $mmd->getMimeNumbers($MESSAGE['structure']);
+
+ if ($_GET['_reply_uid'])
+ {
+ $REPLY_MESSAGE = &$MESSAGE;
+ $_SESSION['compose']['reply_uid'] = $_GET['_reply_uid'];
+ $_SESSION['compose']['reply_msgid'] = $REPLY_MESSAGE['headers']->messageID;
+ $_SESSION['compose']['references'] = $REPLY_MESSAGE['headers']->reference;
+ $_SESSION['compose']['references'] .= !empty($REPLY_MESSAGE['headers']->reference) ? ' ' : '';
+ $_SESSION['compose']['references'] .= $REPLY_MESSAGE['headers']->messageID;
+
+ if ($_GET['_all'])
+ $REPLY_MESSAGE['reply_all'] = 1;
+
+ }
+ else if ($_GET['_forward_uid'])
+ {
+ $FORWARD_MESSAGE = $MESSAGE;
+ $_SESSION['compose']['forward_uid'] = $_GET['_forward_uid'];
+ }
+ else
+ {
+ $DRAFT_MESSAGE = $MESSAGE;
+ $_SESSION['compose']['draft_uid'] = $_GET['_draft_uid'];
+ }
+
+ }
+
+/****** compose mode functions ********/
+
+
+function rcmail_compose_headers($attrib)
+ {
+ global $IMAP, $REPLY_MESSAGE, $DRAFT_MESSAGE, $DB;
+ static $sa_recipients = array();
+
+ list($form_start, $form_end) = get_form_tags($attrib);
+
+ $out = '';
+ $part = strtolower($attrib['part']);
+
+ switch ($part)
+ {
+ case 'from':
+ return rcmail_compose_header_from($attrib);
+
+ case 'to':
+ $fname = '_to';
+ $header = 'to';
+
+ // we have contact id's as get parameters
+ if (!empty($_GET['_to']) && preg_match('/^[0-9]+(,[0-9]+)*$/', $_GET['_to']))
+ {
+ $a_recipients = array();
+ $sql_result = $DB->query("SELECT name, email
+ FROM ".get_table_name('contacts')."
+ WHERE user_id=?
+ AND del<>1
+ AND contact_id IN (".$_GET['_to'].")",
+ $_SESSION['user_id']);
+
+ while ($sql_arr = $DB->fetch_assoc($sql_result))
+ $a_recipients[] = format_email_recipient($sql_arr['email'], $sql_arr['name']);
+
+ if (sizeof($a_recipients))
+ $fvalue = join(', ', $a_recipients);
+ }
+ else if (!empty($_GET['_to']))
+ $fvalue = $_GET['_to'];
+
+ case 'cc':
+ if (!$fname)
+ {
+ $fname = '_cc';
+ $header = 'cc';
+ }
+ case 'bcc':
+ if (!$fname)
+ $fname = '_bcc';
+
+ $allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'wrap', 'tabindex');
+ $field_type = 'textarea';
+ break;
+
+ case 'replyto':
+ case 'reply-to':
+ $fname = '_replyto';
+ $allow_attrib = array('id', 'class', 'style', 'size', 'tabindex');
+ $field_type = 'textfield';
+ break;
+
+ }
+
+ if ($fname && !empty($_POST[$fname]))
+ $fvalue = get_input_value($fname, RCUBE_INPUT_POST, TRUE);
+ else if ($header && is_object($REPLY_MESSAGE['headers']))
+ {
+ // get recipent address(es) out of the message headers
+ if ($header=='to' && $REPLY_MESSAGE['headers']->replyto)
+ $fvalue = $IMAP->decode_header($REPLY_MESSAGE['headers']->replyto);
+
+ else if ($header=='to' && $REPLY_MESSAGE['headers']->from)
+ $fvalue = $IMAP->decode_header($REPLY_MESSAGE['headers']->from);
+
+ // add recipent of original message if reply to all
+ else if ($header=='cc' && $REPLY_MESSAGE['reply_all'])
+ {
+ if ($IMAP->decode_header($REPLY_MESSAGE['headers']->to))
+ $fvalue .= $IMAP->decode_header($REPLY_MESSAGE['headers']->to);
+
+ if ($IMAP->decode_header($REPLY_MESSAGE['headers']->cc))
+ {
+ if($fvalue)
+ $fvalue .= ', ';
+
+ $fvalue .= $IMAP->decode_header($REPLY_MESSAGE['headers']->cc);
+ }
+ }
+
+ // split recipients and put them back together in a unique way
+ if (!empty($fvalue))
+ {
+ $to_addresses = $IMAP->decode_address_list($fvalue);
+ $fvalue = '';
+ foreach ($to_addresses as $addr_part)
+ {
+ if (!in_array($addr_part['mailto'], $sa_recipients) && (!$REPLY_MESSAGE['FROM'] || !in_array($addr_part['mailto'], $REPLY_MESSAGE['FROM'])))
+ {
+ $fvalue .= (strlen($fvalue) ? ', ':'').$addr_part['string'];
+ $sa_recipients[] = $addr_part['mailto'];
+ }
+ }
+ }
+ }
+ else if ($header && is_object($DRAFT_MESSAGE['headers']))
+ {
+ // get drafted headers
+ if ($header=='to' && $DRAFT_MESSAGE['headers']->to)
+ $fvalue = $IMAP->decode_header($DRAFT_MESSAGE['headers']->to);
+
+ if ($header=='cc' && $DRAFT_MESSAGE['headers']->cc)
+ $fvalue = $IMAP->decode_header($DRAFT_MESSAGE['headers']->cc);
+
+ if ($header=='bcc' && $DRAFT_MESSAGE['headers']->bcc)
+ $fvalue = $IMAP->decode_header($DRAFT_MESSAGE['headers']->bcc);
+
+ }
+
+
+ if ($fname && $field_type)
+ {
+ // pass the following attributes to the form class
+ $field_attrib = array('name' => $fname);
+ foreach ($attrib as $attr => $value)
+ if (in_array($attr, $allow_attrib))
+ $field_attrib[$attr] = $value;
+
+ // create teaxtarea object
+ $input = new $field_type($field_attrib);
+ $out = $input->show($fvalue);
+ }
+
+ if ($form_start)
+ $out = $form_start.$out;
+
+ return $out;
+ }
+
+
+
+function rcmail_compose_header_from($attrib)
+ {
+ global $IMAP, $REPLY_MESSAGE, $DRAFT_MESSAGE, $DB, $OUTPUT, $JS_OBJECT_NAME;
+
+ // 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;
+
+ // extract all recipients of the reply-message
+ $a_recipients = array();
+ if ($REPLY_MESSAGE && is_object($REPLY_MESSAGE['headers']))
+ {
+ $REPLY_MESSAGE['FROM'] = array();
+
+ $a_to = $IMAP->decode_address_list($REPLY_MESSAGE['headers']->to);
+ foreach ($a_to as $addr)
+ {
+ if (!empty($addr['mailto']))
+ $a_recipients[] = $addr['mailto'];
+ }
+
+ if (!empty($REPLY_MESSAGE['headers']->cc))
+ {
+ $a_cc = $IMAP->decode_address_list($REPLY_MESSAGE['headers']->cc);
+ foreach ($a_cc as $addr)
+ {
+ if (!empty($addr['mailto']))
+ $a_recipients[] = $addr['mailto'];
+ }
+ }
+ }
+
+ // get this user's identities
+ $sql_result = $DB->query("SELECT identity_id, name, email, signature
+ FROM ".get_table_name('identities')."
+ WHERE user_id=?
+ AND del<>1
+ ORDER BY ".$DB->quoteIdentifier('standard')." DESC, name ASC",
+ $_SESSION['user_id']);
+
+ if ($DB->num_rows($sql_result))
+ {
+ $from_id = 0;
+ $a_signatures = array();
+
+ $field_attrib['onchange'] = "$JS_OBJECT_NAME.change_identity(this)";
+ $select_from = new select($field_attrib);
+
+ while ($sql_arr = $DB->fetch_assoc($sql_result))
+ {
+ $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $sql_arr['identity_id']);
+
+ // add signature to array
+ if (!empty($sql_arr['signature']))
+ $a_signatures[$sql_arr['identity_id']] = $sql_arr['signature'];
+
+ // set identity if it's one of the reply-message recipients
+ if (in_array($sql_arr['email'], $a_recipients))
+ $from_id = $sql_arr['identity_id'];
+
+ if ($REPLY_MESSAGE && is_array($REPLY_MESSAGE['FROM']))
+ $REPLY_MESSAGE['FROM'][] = $sql_arr['email'];
+
+ if (strstr($DRAFT_MESSAGE['headers']->from,$sql_arr['email']))
+ $from_id = $sql_arr['identity_id'];
+
+ }
+
+ // overwrite identity selection with post parameter
+ if (isset($_POST['_from']))
+ $from_id = $_POST['_from'];
+
+ $out = $select_from->show($from_id);
+
+
+ // add signatures to client
+ $OUTPUT->add_script(sprintf("%s.set_env('signatures', %s);", $JS_OBJECT_NAME, array2js($a_signatures)));
+ }
+ else
+ {
+ $input_from = new textfield($field_attrib);
+ $out = $input_from->show($_POST['_from']);
+ }
+
+ if ($form_start)
+ $out = $form_start.$out;
+
+ return $out;
+ }
+
+
+
+function rcmail_compose_body($attrib)
+ {
+ global $CONFIG, $OUTPUT, $REPLY_MESSAGE, $FORWARD_MESSAGE, $DRAFT_MESSAGE, $JS_OBJECT_NAME;
+
+ list($form_start, $form_end) = get_form_tags($attrib);
+ unset($attrib['form']);
+
+ if (empty($attrib['id']))
+ $attrib['id'] = 'rcmComposeMessage';
+
+ $attrib['name'] = '_message';
+ $textarea = new textarea($attrib);
+
+ $body = '';
+
+ // use posted message body
+ if (!empty($_POST['_message']))
+ $body = get_input_value('_message', RCUBE_INPUT_POST, TRUE);
+
+ // compose reply-body
+ else if (is_array($REPLY_MESSAGE['parts']))
+ {
+ $body = rcmail_first_text_part($REPLY_MESSAGE['parts']);
+ if (strlen($body))
+ $body = rcmail_create_reply_body($body);
+ }
+
+ // forward message body inline
+ else if (is_array($FORWARD_MESSAGE['parts']))
+ {
+ $body = rcmail_first_text_part($FORWARD_MESSAGE['parts']);
+ if (strlen($body))
+ $body = rcmail_create_forward_body($body);
+ }
+
+ // forward message body inline
+ else if (is_array($DRAFT_MESSAGE['parts']))
+ {
+ $body = rcmail_first_text_part($DRAFT_MESSAGE['parts']);
+ if (strlen($body))
+ $body = rcmail_create_draft_body($body);
+ }
+
+ $out = $form_start ? "$form_start\n" : '';
+
+ $saveid = new hiddenfield(array('name' => '_draft_saveid', 'value' => str_replace(array('<','>'),"",$DRAFT_MESSAGE['headers']->messageID) ));
+ $out .= $saveid->show();
+
+ $drafttoggle = new hiddenfield(array('name' => '_draft', 'value' => 'yes'));
+ $out .= $drafttoggle->show();
+
+ $out .= $textarea->show($body);
+ $out .= $form_end ? "\n$form_end" : '';
+
+ // include GoogieSpell
+ if (!empty($CONFIG['enable_spellcheck']))
+ {
+ $OUTPUT->include_script('googiespell.js');
+ $OUTPUT->add_script(sprintf("var googie = new GoogieSpell('\$__skin_path/images/googiespell/','%s&_action=spell&lang=');\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.decorateTextarea('%s');\n".
+ "%s.set_env('spellcheck', googie);",
+ $GLOBALS['COMM_PATH'],
+ rep_specialchars_output(rcube_label('checkspelling')),
+ rep_specialchars_output(rcube_label('resumeediting')),
+ rep_specialchars_output(rcube_label('close')),
+ rep_specialchars_output(rcube_label('revertto')),
+ rep_specialchars_output(rcube_label('nospellerrors')),
+ $attrib['id'],
+ $JS_OBJECT_NAME), 'foot');
+
+ rcube_add_label('checking');
+ }
+
+ $out .= "\n".'<iframe name="savetarget" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
+
+ return $out;
+ }
+
+
+function rcmail_create_reply_body($body)
+ {
+ global $IMAP, $REPLY_MESSAGE;
+
+ // soft-wrap message first
+ $body = wordwrap($body, 75);
+
+ // split body into single lines
+ $a_lines = preg_split('/\r?\n/', $body);
+
+ // add > to each line
+ for($n=0; $n<sizeof($a_lines); $n++)
+ {
+ if (strpos($a_lines[$n], '>')===0)
+ $a_lines[$n] = '>'.$a_lines[$n];
+ else
+ $a_lines[$n] = '> '.$a_lines[$n];
+ }
+
+ $body = join("\n", $a_lines);
+
+ // add title line
+ $pefix = sprintf("\n\n\nOn %s, %s wrote:\n",
+ $REPLY_MESSAGE['headers']->date,
+ $IMAP->decode_header($REPLY_MESSAGE['headers']->from));
+
+
+ // try to remove the signature
+ if ($sp = strrpos($body, '-- '))
+ {
+ if ($body{$sp+3}==' ' || $body{$sp+3}=="\n" || $body{$sp+3}=="\r")
+ $body = substr($body, 0, $sp-1);
+ }
+
+ return $pefix.$body;
+ }
+
+
+function rcmail_create_forward_body($body)
+ {
+ global $IMAP, $FORWARD_MESSAGE;
+
+ // soft-wrap message first
+ $body = wordwrap($body, 80);
+
+ $prefix = sprintf("\n\n\n-------- Original Message --------\nSubject: %s\nDate: %s\nFrom: %s\nTo: %s\n\n",
+ $FORWARD_MESSAGE['subject'],
+ $FORWARD_MESSAGE['headers']->date,
+ $IMAP->decode_header($FORWARD_MESSAGE['headers']->from),
+ $IMAP->decode_header($FORWARD_MESSAGE['headers']->to));
+
+ // add attachments
+ if (!isset($_SESSION['compose']['forward_attachments']) && is_array($FORWARD_MESSAGE['parts']) && sizeof($FORWARD_MESSAGE['parts'])>1)
+ {
+ $temp_dir = rcmail_create_compose_tempdir();
+
+ if (!is_array($_SESSION['compose']['attachments']))
+ $_SESSION['compose']['attachments'] = array();
+
+ foreach ($FORWARD_MESSAGE['parts'] as $part)
+ {
+ if ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id'] ||
+ (empty($part->disposition) && ($part->d_parameters['filename'] || $part->ctype_parameters['name'])))
+ {
+ $tmp_path = tempnam($temp_dir, 'rcmAttmnt');
+ if ($fp = fopen($tmp_path, 'w'))
+ {
+ fwrite($fp, $IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding']));
+ fclose($fp);
+
+ if ($part->d_parameters['filename'])
+ $_SESSION['compose']['attachments'][] = array('name' => $part->d_parameters['filename'],
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'path' => $tmp_path);
+
+ else if ($part->ctype_parameters['name'])
+ $_SESSION['compose']['attachments'][] = array('name' => $part->ctype_parameters['name'],
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'path' => $tmp_path);
+
+ else if ($part->headers['content-description'])
+ $_SESSION['compose']['attachments'][] = array('name' => $part->headers['content-description'],
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'path' => $tmp_path);
+ }
+ }
+ }
+
+ $_SESSION['compose']['forward_attachments'] = TRUE;
+ }
+
+ return $prefix.$body;
+ }
+
+function rcmail_create_draft_body($body)
+ {
+ global $IMAP, $DRAFT_MESSAGE;
+
+ // add attachments
+ if (!isset($_SESSION['compose']['forward_attachments']) && is_array($DRAFT_MESSAGE['parts']) && sizeof($DRAFT_MESSAGE['parts'])>1)
+ {
+ $temp_dir = rcmail_create_compose_tempdir();
+
+ if (!is_array($_SESSION['compose']['attachments']))
+ $_SESSION['compose']['attachments'] = array();
+
+ foreach ($DRAFT_MESSAGE['parts'] as $part)
+ {
+ if ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id'] ||
+ (empty($part->disposition) && ($part->d_parameters['filename'] || $part->ctype_parameters['name'])))
+ {
+ $tmp_path = tempnam($temp_dir, 'rcmAttmnt');
+ if ($fp = fopen($tmp_path, 'w'))
+ {
+ fwrite($fp, $IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding']));
+ fclose($fp);
+
+ if ($part->d_parameters['filename'])
+ $_SESSION['compose']['attachments'][] = array('name' => $part->d_parameters['filename'],
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'path' => $tmp_path);
+
+ else if ($part->ctype_parameters['name'])
+ $_SESSION['compose']['attachments'][] = array('name' => $part->ctype_parameters['name'],
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'path' => $tmp_path);
+
+ else if ($part->headers['content-description'])
+ $_SESSION['compose']['attachments'][] = array('name' => $part->headers['content-description'],
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'path' => $tmp_path);
+ }
+ }
+ }
+
+ $_SESSION['compose']['forward_attachments'] = TRUE;
+ }
+
+ return $body;
+ }
+
+
+function rcmail_compose_subject($attrib)
+ {
+ global $CONFIG, $REPLY_MESSAGE, $FORWARD_MESSAGE, $DRAFT_MESSAGE;
+
+ list($form_start, $form_end) = get_form_tags($attrib);
+ unset($attrib['form']);
+
+ $attrib['name'] = '_subject';
+ $textfield = new textfield($attrib);
+
+ $subject = '';
+
+ // use subject from post
+ if (isset($_POST['_subject']))
+ $subject = get_input_value('_subject', RCUBE_INPUT_POST, TRUE);
+
+ // create a reply-subject
+ else if (isset($REPLY_MESSAGE['subject']))
+ {
+ if (eregi('^re:', $REPLY_MESSAGE['subject']))
+ $subject = $REPLY_MESSAGE['subject'];
+ else
+ $subject = 'Re: '.$REPLY_MESSAGE['subject'];
+ }
+
+ // create a forward-subject
+ else if (isset($FORWARD_MESSAGE['subject']))
+ {
+ if (eregi('^fwd:', $REPLY_MESSAGE['subject']))
+ $subject = $FORWARD_MESSAGE['subject'];
+ else
+ $subject = 'Fwd: '.$FORWARD_MESSAGE['subject'];
+ }
+
+ // creeate a draft-subject
+ else if (isset($DRAFT_MESSAGE['subject']))
+ $subject = $DRAFT_MESSAGE['subject'];
+
+ $out = $form_start ? "$form_start\n" : '';
+ $out .= $textfield->show($subject);
+ $out .= $form_end ? "\n$form_end" : '';
+
+ return $out;
+ }
+
+
+function rcmail_compose_attachment_list($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ // add ID if not given
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmAttachmentList';
+
+ // allow the following attributes to be added to the <ul> tag
+ $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style'));
+
+ $out = '<ul'. $attrib_str . ">\n";
+
+ if (is_array($_SESSION['compose']['attachments']))
+ {
+ if ($attrib['deleteicon'])
+ $button = sprintf('<img src="%s%s" alt="%s" border="0" style="padding-right:2px;vertical-align:middle" />',
+ $CONFIG['skin_path'],
+ $attrib['deleteicon'],
+ rcube_label('delete'));
+ else
+ $button = rcube_label('delete');
+
+ foreach ($_SESSION['compose']['attachments'] as $id => $a_prop)
+ $out .= sprintf('<li id="rcmfile%d"><a href="#delete" onclick="return %s.command(\'remove-attachment\',\'rcmfile%d\', this)" title="%s">%s</a>%s</li>',
+ $id,
+ $JS_OBJECT_NAME,
+ $id,
+ rcube_label('delete'),
+ $button,
+ rep_specialchars_output($a_prop['name']));
+ }
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('attachmentlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+
+ $out .= '</ul>';
+ return $out;
+ }
+
+
+
+function rcmail_compose_attachment_form($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD;
+
+ // add ID if not given
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmUploadbox';
+
+ // allow the following attributes to be added to the <div> tag
+ $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style'));
+ $input_field = rcmail_compose_attachment_field(array());
+ $label_send = rcube_label('upload');
+ $label_close = rcube_label('close');
+
+ $out = <<<EOF
+<div$attrib_str>
+<form action="./" method="post" enctype="multipart/form-data">
+$SESS_HIDDEN_FIELD
+$input_field<br />
+<input type="button" value="$label_close" class="button" onclick="document.getElementById('$attrib[id]').style.visibility='hidden'" />
+<input type="button" value="$label_send" class="button" onclick="$JS_OBJECT_NAME.command('send-attachment', this.form)" />
+</form>
+</div>
+EOF;
+
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('uploadbox', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+ return $out;
+ }
+
+
+function rcmail_compose_attachment_field($attrib)
+ {
+ // allow the following attributes to be added to the <input> tag
+ $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'size'));
+
+ $out = '<input type="file" name="_attachments[]"'. $attrib_str . " />";
+ return $out;
+ }
+
+
+function rcmail_priority_selector($attrib)
+ {
+ list($form_start, $form_end) = get_form_tags($attrib);
+ unset($attrib['form']);
+
+ $attrib['name'] = '_priority';
+ $selector = new select($attrib);
+
+ $selector->add(array(rcube_label('lowest'),
+ rcube_label('low'),
+ rcube_label('normal'),
+ rcube_label('high'),
+ rcube_label('highest')),
+ array(5, 4, 0, 2, 1));
+
+ $sel = isset($_POST['_priority']) ? $_POST['_priority'] : 0;
+
+ $out = $form_start ? "$form_start\n" : '';
+ $out .= $selector->show($sel);
+ $out .= $form_end ? "\n$form_end" : '';
+
+ return $out;
+ }
+
+
+function rcmail_receipt_checkbox($attrib)
+ {
+ 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 checkbox($attrib);
+
+ $out = $form_start ? "$form_start\n" : '';
+ $out .= $checkbox->show(0);
+ $out .= $form_end ? "\n$form_end" : '';
+
+ return $out;
+ }
+
+
+function get_form_tags($attrib)
+ {
+ global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $MESSAGE_FORM, $SESS_HIDDEN_FIELD;
+
+ $form_start = '';
+ if (!strlen($MESSAGE_FORM))
+ {
+ $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task']));
+ $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
+
+ $form_start = empty($attrib['form']) ? '<form name="form" action="./" method="post">' : '';
+ $form_start .= "\n$SESS_HIDDEN_FIELD\n";
+ $form_start .= $hiddenfields->show();
+ }
+
+ $form_end = (strlen($MESSAGE_FORM) && !strlen($attrib['form'])) ? '</form>' : '';
+ $form_name = !empty($attrib['form']) ? $attrib['form'] : 'form';
+
+ if (!strlen($MESSAGE_FORM))
+ $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('messageform', '$form_name');");
+
+ $MESSAGE_FORM = $form_name;
+
+ return array($form_start, $form_end);
+ }
+
+
+function format_email_recipient($email, $name='')
+ {
+ if ($name && $name != $email)
+ return sprintf('%s <%s>', strpos($name, ",") ? '"'.$name.'"' : $name, $email);
+ else
+ return $email;
+ }
+
+
+function rcmail_charset_pulldown($selected='ISO-8859-1')
+ {
+ $select = new select();
+
+
+ return $select->show($selected);
+ }
+
+
+/****** get contacts for this user and add them to client scripts ********/
+
+$sql_result = $DB->query("SELECT name, email
+ FROM ".get_table_name('contacts')." WHERE user_id=?
+ AND del<>1",$_SESSION['user_id']);
+
+if ($DB->num_rows($sql_result))
+ {
+ $a_contacts = array();
+ while ($sql_arr = $DB->fetch_assoc($sql_result))
+ if ($sql_arr['email'])
+ $a_contacts[] = format_email_recipient($sql_arr['email'], rep_specialchars_output($sql_arr['name'], 'js'));
+
+ $OUTPUT->add_script(sprintf("$JS_OBJECT_NAME.set_env('contacts', %s);", array2js($a_contacts)));
+ }
+
+
+parse_template('compose');
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/folders.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Implement folder operations line EXPUNGE and Clear |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: folders.inc 232 2006-05-18 15:46:50Z cmcnulty $
+*/
+
+$REMOTE_REQUEST = TRUE;
+$mbox_name = $IMAP->get_mailbox_name();
+
+
+// send EXPUNGE command
+if ($_action=='expunge')
+ {
+ $success = $IMAP->expunge($_GET['_mbox']);
+
+ // reload message list if current mailbox
+ if ($success && $_GET['_reload'])
+ {
+ rcube_remote_response('this.clear_message_list();', TRUE);
+ $_action = 'list';
+ return;
+ }
+ else
+ $commands = "// expunged: $success\n";
+ }
+
+// clear mailbox
+else if ($_action=='purge')
+ {
+ $success = $IMAP->clear_mailbox($_GET['_mbox']);
+
+ if ($success && $_GET['_reload'])
+ {
+ $commands = "this.clear_message_list();\n";
+ $commands .= "this.set_env('messagecount', 0);\n";
+ $commands .= "this.set_env('pagecount', 0);\n";
+ $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text());
+ $commands .= sprintf("this.set_unread_count('%s', 0);\n", addslashes($mbox_name));
+ }
+ else
+ $commands = "// purged: $success";
+ }
+
+
+
+rcube_remote_response($commands);
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/func.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide webmail functionality and GUI objects |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: func.inc 429 2006-12-22 22:26:24Z thomasb $
+
+*/
+
+require_once('lib/html2text.inc');
+require_once('lib/enriched.inc');
+
+
+$EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
+
+if (empty($_SESSION['mbox'])){
+ $_SESSION['mbox'] = $IMAP->get_mailbox_name();
+}
+
+// set imap properties and session vars
+if (strlen($_GET['_mbox']))
+ {
+ $IMAP->set_mailbox($_GET['_mbox']);
+ $_SESSION['mbox'] = $_GET['_mbox'];
+ }
+
+if (strlen($_GET['_page']))
+ {
+ $IMAP->set_page($_GET['_page']);
+ $_SESSION['page'] = $_GET['_page'];
+ }
+
+// set mailbox to INBOX if not set
+if (empty($_SESSION['mbox']))
+ $_SESSION['mbox'] = $IMAP->get_mailbox_name();
+
+// set default sort col/order to session
+if (!isset($_SESSION['sort_col']))
+ $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
+if (!isset($_SESSION['sort_order']))
+ $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
+
+
+// define url for getting message parts
+if (strlen($_GET['_uid']))
+ $GET_URL = sprintf('%s&_action=get&_mbox=%s&_uid=%d', $COMM_PATH, $IMAP->get_mailbox_name(), $_GET['_uid']);
+
+
+// set current mailbox in client environment
+$OUTPUT->add_script(sprintf("%s.set_env('mailbox', '%s');", $JS_OBJECT_NAME, $IMAP->get_mailbox_name()));
+
+if ($CONFIG['trash_mbox'])
+ $OUTPUT->add_script(sprintf("%s.set_env('trash_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['trash_mbox']));
+
+if ($CONFIG['drafts_mbox'])
+ $OUTPUT->add_script(sprintf("%s.set_env('drafts_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['drafts_mbox']));
+
+if ($CONFIG['junk_mbox'])
+ $OUTPUT->add_script(sprintf("%s.set_env('junk_mailbox', '%s');", $JS_OBJECT_NAME, $CONFIG['junk_mbox']));
+
+// return the mailboxlist in HTML
+function rcmail_mailbox_list($attrib)
+ {
+ global $IMAP, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $COMM_PATH;
+ static $s_added_script = FALSE;
+ static $a_mailboxes;
+
+ // add some labels to client
+ rcube_add_label('purgefolderconfirm');
+
+// $mboxlist_start = rcube_timer();
+
+ $type = $attrib['type'] ? $attrib['type'] : 'ul';
+ $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
+ array('style', 'class', 'id');
+
+ if ($type=='ul' && !$attrib['id'])
+ $attrib['id'] = 'rcmboxlist';
+
+ // allow the following attributes to be added to the <ul> tag
+ $attrib_str = create_attrib_string($attrib, $add_attrib);
+
+ $out = '<' . $type . $attrib_str . ">\n";
+
+ // add no-selection option
+ if ($type=='select' && $attrib['noselection'])
+ $out .= sprintf('<option value="0">%s</option>'."\n",
+ rcube_label($attrib['noselection']));
+
+ // get mailbox list
+ $mbox_name = $IMAP->get_mailbox_name();
+
+ // for these mailboxes we have localized labels
+ $special_mailboxes = array('inbox', 'sent', 'drafts', 'trash', 'junk');
+
+
+ // build the folders tree
+ if (empty($a_mailboxes))
+ {
+ // get mailbox list
+ $a_folders = $IMAP->list_mailboxes();
+ $delimiter = $IMAP->get_hierarchy_delimiter();
+ $a_mailboxes = array();
+
+// rcube_print_time($mboxlist_start, 'list_mailboxes()');
+
+ foreach ($a_folders as $folder)
+ rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
+ }
+
+// var_dump($a_mailboxes);
+
+ if ($type=='select')
+ $out .= rcmail_render_folder_tree_select($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
+ else
+ $out .= rcmail_render_folder_tree_html($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
+
+// rcube_print_time($mboxlist_start, 'render_folder_tree()');
+
+
+ if ($type=='ul')
+ $OUTPUT->add_script(sprintf("%s.gui_object('mailboxlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+
+ return $out . "</$type>";
+ }
+
+
+
+
+// create a hierarchical array of the mailbox list
+function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
+ {
+ $pos = strpos($folder, $delm);
+ if ($pos !== false)
+ {
+ $subFolders = substr($folder, $pos+1);
+ $currentFolder = substr($folder, 0, $pos);
+ }
+ else
+ {
+ $subFolders = false;
+ $currentFolder = $folder;
+ }
+
+ $path .= $currentFolder;
+
+ if (!isset($arrFolders[$currentFolder]))
+ {
+ $arrFolders[$currentFolder] = array('id' => $path,
+ 'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
+ 'folders' => array());
+ }
+
+ if (!empty($subFolders))
+ rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
+ }
+
+
+// return html for a structured list <ul> for the mailbox tree
+function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
+ {
+ global $JS_OBJECT_NAME, $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
+
+ $idx = 0;
+ $out = '';
+ foreach ($arrFolders as $key => $folder)
+ {
+ $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
+ $title = '';
+
+ $folder_lc = strtolower($folder['id']);
+ if (in_array($folder_lc, $special))
+ $foldername = rcube_label($folder_lc);
+ else
+ {
+ $foldername = $folder['name'];
+
+ // shorten the folder name to a given length
+ if ($maxlength && $maxlength>1)
+ {
+ $fname = abbrevate_string($foldername, $maxlength);
+ if ($fname != $foldername)
+ $title = ' title="'.rep_specialchars_output($foldername, 'html', 'all').'"';
+ $foldername = $fname;
+ }
+ }
+
+ // add unread message count display
+ if ($unread_count = $IMAP->messagecount($folder['id'], 'RECENT', ($folder['id']==$mbox_name)))
+ $foldername .= sprintf(' (%d)', $unread_count);
+
+ // make folder name safe for ids and class names
+ $folder_css = $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_lc);
+
+ // set special class for Sent, Drafts, Trash and Junk
+ if ($folder['id']==$CONFIG['sent_mbox'])
+ $class_name = 'sent';
+ else if ($folder['id']==$CONFIG['drafts_mbox'])
+ $class_name = 'drafts';
+ else if ($folder['id']==$CONFIG['trash_mbox'])
+ $class_name = 'trash';
+ else if ($folder['id']==$CONFIG['junk_mbox'])
+ $class_name = 'junk';
+
+ $out .= sprintf('<li id="rcmbx%s" class="mailbox %s %s%s%s"><a href="%s&_mbox=%s"'.
+ ' onclick="return %s.command(\'list\',\'%s\')"'.
+ ' onmouseover="return %s.focus_mailbox(\'%s\')"' .
+ ' onmouseout="return %s.unfocus_mailbox(\'%s\')"' .
+ ' onmouseup="return %s.mbox_mouse_up(\'%s\')"%s>%s</a>',
+ $folder_css,
+ $class_name,
+ $zebra_class,
+ $unread_count ? ' unread' : '',
+ addslashes($folder['id'])==addslashes($mbox_name) ? ' selected' : '',
+ $COMM_PATH,
+ urlencode($folder['id']),
+ $JS_OBJECT_NAME,
+ addslashes($folder['id']),
+ $JS_OBJECT_NAME,
+ addslashes($folder['id']),
+ $JS_OBJECT_NAME,
+ addslashes($folder['id']),
+ $JS_OBJECT_NAME,
+ addslashes($folder['id']),
+ $title,
+ rep_specialchars_output($foldername, 'html', 'all'));
+
+ if (!empty($folder['folders']))
+ $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
+
+ $out .= "</li>\n";
+ $idx++;
+ }
+
+ return $out;
+ }
+
+
+// return html for a flat list <select> for the mailbox tree
+function rcmail_render_folder_tree_select(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
+ {
+ global $IMAP, $OUTPUT;
+
+ $idx = 0;
+ $out = '';
+ foreach ($arrFolders as $key=>$folder)
+ {
+ $folder_lc = strtolower($folder['id']);
+ if (in_array($folder_lc, $special))
+ $foldername = rcube_label($folder_lc);
+ else
+ {
+ $foldername = $folder['name'];
+
+ // shorten the folder name to a given length
+ if ($maxlength && $maxlength>1)
+ $foldername = abbrevate_string($foldername, $maxlength);
+ }
+
+ $out .= sprintf('<option value="%s">%s%s</option>'."\n",
+ $folder['id'],
+ str_repeat(' ', $nestLevel*4),
+ rep_specialchars_output($foldername, 'html', 'all'));
+
+ if (!empty($folder['folders']))
+ $out .= rcmail_render_folder_tree_select($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1);
+
+ $idx++;
+ }
+
+ return $out;
+ }
+
+
+// return the message list as HTML table
+function rcmail_message_list($attrib)
+ {
+ global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT, $JS_OBJECT_NAME;
+
+ $skin_path = $CONFIG['skin_path'];
+ $image_tag = '<img src="%s%s" alt="%s" border="0" />';
+
+ // check to see if we have some settings for sorting
+ $sort_col = $_SESSION['sort_col'];
+ $sort_order = $_SESSION['sort_order'];
+
+ // add some labels to client
+ rcube_add_label('from', 'to');
+
+ // get message headers
+ $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
+
+ // add id to message list table if not specified
+ if (!strlen($attrib['id']))
+ $attrib['id'] = 'rcubemessagelist';
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+
+ $out = '<table' . $attrib_str . ">\n";
+
+
+ // define list of cols to be displayed
+ $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
+ $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
+
+ // show 'to' instead of from in sent messages
+ if (($IMAP->get_mailbox_name()==$CONFIG['sent_mbox'] || $IMAP->get_mailbox_name()==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
+ && !array_search('to', $a_show_cols))
+ $a_show_cols[$f] = 'to';
+
+ // add col definition
+ $out .= '<colgroup>';
+ $out .= '<col class="icon" />';
+
+ foreach ($a_show_cols as $col)
+ $out .= sprintf('<col class="%s" />', $col);
+
+ $out .= '<col class="icon" />';
+ $out .= "</colgroup>\n";
+
+ // add table title
+ $out .= "<thead><tr>\n<td class=\"icon\"> </td>\n";
+
+ $javascript = '';
+ foreach ($a_show_cols as $col)
+ {
+ // get column name
+ $col_name = rep_specialchars_output(rcube_label($col));
+
+ // make sort links
+ $sort = '';
+ if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
+ {
+ // have buttons configured
+ if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
+ {
+ $sort = ' ';
+
+ // asc link
+ if (!empty($attrib['sortascbutton']))
+ {
+ $sort .= rcube_button(array('command' => 'sort',
+ 'prop' => $col.'_ASC',
+ 'image' => $attrib['sortascbutton'],
+ 'align' => 'absmiddle',
+ 'title' => 'sortasc'));
+ }
+
+ // desc link
+ if (!empty($attrib['sortdescbutton']))
+ {
+ $sort .= rcube_button(array('command' => 'sort',
+ 'prop' => $col.'_DESC',
+ 'image' => $attrib['sortdescbutton'],
+ 'align' => 'absmiddle',
+ 'title' => 'sortdesc'));
+ }
+ }
+ // just add a link tag to the header
+ else
+ {
+ $col_name = sprintf('<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
+ $JS_OBJECT_NAME,
+ $col,
+ rcube_label('sortby'),
+ $col_name);
+ }
+ }
+
+ $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
+
+ // put it all together
+ $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";
+ }
+
+ $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
+ $out .= "</tr></thead>\n<tbody>\n";
+
+ // no messages in this mailbox
+ if (!sizeof($a_headers))
+ {
+ $out .= rep_specialchars_output(
+ sprintf('<tr><td colspan="%d">%s</td></tr>',
+ sizeof($a_show_cols)+2,
+ rcube_label('nomessagesfound')));
+ }
+
+
+ $a_js_message_arr = array();
+
+ // create row for each message
+ foreach ($a_headers as $i => $header) //while (list($i, $header) = each($a_headers))
+ {
+ $message_icon = $attach_icon = '';
+ $js_row_arr = array();
+ $zebra_class = $i%2 ? 'even' : 'odd';
+
+ // set messag attributes to javascript array
+ if ($header->deleted)
+ $js_row_arr['deleted'] = true;
+ if (!$header->seen)
+ $js_row_arr['unread'] = true;
+ if ($header->answered)
+ $js_row_arr['replied'] = true;
+ // set message icon
+ if ($attrib['deletedicon'] && $header->deleted)
+ $message_icon = $attrib['deletedicon'];
+ else if ($attrib['unreadicon'] && !$header->seen)
+ $message_icon = $attrib['unreadicon'];
+ else if ($attrib['repliedicon'] && $header->answered)
+ $message_icon = $attrib['repliedicon'];
+ else if ($attrib['messageicon'])
+ $message_icon = $attrib['messageicon'];
+
+ // set attachment icon
+ if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype))
+ $attach_icon = $attrib['attachmenticon'];
+
+ $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
+ $header->uid,
+ $header->seen ? '' : ' unread',
+ $header->deleted ? ' deleted' : '',
+ $zebra_class);
+
+ $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
+
+ // format each col
+ foreach ($a_show_cols as $col)
+ {
+ if ($col=='from' || $col=='to')
+ $cont = rep_specialchars_output(rcmail_address_string($header->$col, 3, $attrib['addicon']));
+ else if ($col=='subject')
+ {
+ $cont = rep_specialchars_output($IMAP->decode_header($header->$col), 'html', 'all');
+ // firefox/mozilla temporary workaround to pad subject with content so that whitespace in rows responds to drag+drop
+ $cont .= '<img src="./program/blank.gif" height="5" width="1000" alt="" />';
+ }
+ else if ($col=='size')
+ $cont = show_bytes($header->$col);
+ else if ($col=='date')
+ $cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date));
+ else
+ $cont = rep_specialchars_output($header->$col, 'html', 'all');
+
+ $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
+ }
+
+ $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
+ $out .= "</tr>\n";
+
+ if (sizeof($js_row_arr))
+ $a_js_message_arr[$header->uid] = $js_row_arr;
+ }
+
+ // complete message table
+ $out .= "</tbody></table>\n";
+
+
+ $message_count = $IMAP->messagecount();
+
+ // set client env
+ $javascript .= sprintf("%s.gui_object('mailcontframe', '%s');\n", $JS_OBJECT_NAME, 'mailcontframe');
+ $javascript .= sprintf("%s.gui_object('messagelist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
+ $javascript .= sprintf("%s.set_env('messagecount', %d);\n", $JS_OBJECT_NAME, $message_count);
+ $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $IMAP->list_page);
+ $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($message_count/$IMAP->page_size));
+ $javascript .= sprintf("%s.set_env('sort_col', '%s');\n", $JS_OBJECT_NAME, $sort_col);
+ $javascript .= sprintf("%s.set_env('sort_order', '%s');\n", $JS_OBJECT_NAME, $sort_order);
+
+ if ($attrib['messageicon'])
+ $javascript .= sprintf("%s.set_env('messageicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['messageicon']);
+ if ($attrib['deletedicon'])
+ $javascript .= sprintf("%s.set_env('deletedicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['deletedicon']);
+ if ($attrib['unreadicon'])
+ $javascript .= sprintf("%s.set_env('unreadicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['unreadicon']);
+ if ($attrib['repliedicon'])
+ $javascript .= sprintf("%s.set_env('repliedicon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['repliedicon']);
+ if ($attrib['attachmenticon'])
+ $javascript .= sprintf("%s.set_env('attachmenticon', '%s%s');\n", $JS_OBJECT_NAME, $skin_path, $attrib['attachmenticon']);
+
+ $javascript .= sprintf("%s.set_env('messages', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr));
+
+ $OUTPUT->add_script($javascript);
+
+ return $out;
+ }
+
+
+
+
+// return javascript commands to add rows to the message list
+function rcmail_js_message_list($a_headers, $insert_top=FALSE)
+ {
+ global $CONFIG, $IMAP;
+
+ $commands = '';
+ $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
+
+ // show 'to' instead of from in sent messages
+ if (strtolower($IMAP->get_mailbox_name())=='sent' && ($f = array_search('from', $a_show_cols))
+ && !array_search('to', $a_show_cols))
+ $a_show_cols[$f] = 'to';
+
+ $commands .= sprintf("this.set_message_coltypes(%s);\n", array2js($a_show_cols));
+
+ // loop through message headers
+ for ($n=0; $a_headers[$n]; $n++)
+ {
+ $header = $a_headers[$n];
+ $a_msg_cols = array();
+ $a_msg_flags = array();
+
+ // format each col; similar as in rcmail_message_list()
+ foreach ($a_show_cols as $col)
+ {
+ if ($col=='from' || $col=='to')
+ $cont = rep_specialchars_output(rcmail_address_string($header->$col, 3));
+ else if ($col=='subject')
+ $cont = rep_specialchars_output($IMAP->decode_header($header->$col), 'html', 'all');
+ else if ($col=='size')
+ $cont = show_bytes($header->$col);
+ else if ($col=='date')
+ $cont = format_date($header->date); //date('m.d.Y G:i:s', strtotime($header->date));
+ else
+ $cont = rep_specialchars_output($header->$col, 'html', 'all');
+
+ $a_msg_cols[$col] = $cont;
+ }
+
+ $a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
+ $a_msg_flags['unread'] = $header->seen ? 0 : 1;
+ $a_msg_flags['replied'] = $header->answered ? 1 : 0;
+ $commands .= sprintf("this.add_message_row(%s, %s, %s, %b, %b);\n",
+ $header->uid,
+ array2js($a_msg_cols),
+ array2js($a_msg_flags),
+ preg_match("/multipart\/m/i", $header->ctype),
+ $insert_top);
+ }
+
+ return $commands;
+ }
+
+
+// return code for search function
+function rcmail_search_form($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ // add some labels to client
+ rcube_add_label('searching');
+
+ $attrib['name'] = '_q';
+
+ if (empty($attrib['id']))
+ $attrib['id'] = 'rcmqsearchbox';
+
+ $input_q = new textfield($attrib);
+ $out = $input_q->show();
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('qsearchbox', '%s');",
+ $JS_OBJECT_NAME,
+ $attrib['id']));
+
+ // add form tag around text field
+ if (empty($attrib['form']))
+ $out = sprintf('<form name="rcmqsearchform" action="./" '.
+ 'onsubmit="%s.command(\'search\');return false" style="display:inline;">%s</form>',
+ $JS_OBJECT_NAME,
+ $out);
+
+ return $out;
+ }
+
+
+function rcmail_messagecount_display($attrib)
+ {
+ global $IMAP, $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmcountdisplay';
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('countdisplay', '%s');",
+ $JS_OBJECT_NAME,
+ $attrib['id']));
+
+ // allow the following attributes to be added to the <span> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+
+
+ $out = '<span' . $attrib_str . '>';
+ $out .= rcmail_get_messagecount_text();
+ $out .= '</span>';
+ return $out;
+ }
+
+
+function rcmail_quota_display($attrib)
+ {
+ global $IMAP, $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmquotadisplay';
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('quotadisplay', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+
+ // allow the following attributes to be added to the <span> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+
+ if (!$IMAP->get_capability('QUOTA'))
+ $quota_text = rcube_label('unknown');
+ else if (!($quota_text = $IMAP->get_quota()))
+ $quota_text = rcube_label('unlimited');
+
+ $out = '<span' . $attrib_str . '>';
+ $out .= $quota_text;
+ $out .= '</span>';
+ return $out;
+ }
+
+
+function rcmail_get_messagecount_text($count=NULL, $page=NULL)
+ {
+ global $IMAP, $MESSAGE;
+
+ if (isset($MESSAGE['index']))
+ {
+ return rcube_label(array('name' => 'messagenrof',
+ 'vars' => array('nr' => $MESSAGE['index']+1,
+ 'count' => $count!==NULL ? $count : $IMAP->messagecount())));
+ }
+
+ if ($page===NULL)
+ $page = $IMAP->list_page;
+
+ $start_msg = ($page-1) * $IMAP->page_size + 1;
+ $max = $count!==NULL ? $count : $IMAP->messagecount();
+
+ if ($max==0)
+ $out = rcube_label('mailboxempty');
+ else
+ $out = rcube_label(array('name' => 'messagesfromto',
+ 'vars' => array('from' => $start_msg,
+ 'to' => min($max, $start_msg + $IMAP->page_size - 1),
+ 'count' => $max)));
+
+ return rep_specialchars_output($out);
+ }
+
+
+function rcmail_print_body($part, $safe=FALSE, $plain=FALSE) // $body, $ctype_primary='text', $ctype_secondary='plain', $encoding='7bit', $safe=FALSE, $plain=FALSE)
+ {
+ global $IMAP, $REMOTE_OBJECTS, $JS_OBJECT_NAME;
+
+ // extract part properties: body, ctype_primary, ctype_secondary, encoding, parameters
+ extract($part);
+
+ $block = $plain ? '%s' : '%s'; //'<div style="display:block;">%s</div>';
+ $body = $IMAP->mime_decode($body, $encoding);
+ $body = $IMAP->charset_decode($body, $parameters);
+
+ // text/html
+ if ($ctype_secondary=='html')
+ {
+ if (!$safe) // remove remote images and scripts
+ {
+ $remote_patterns = array('/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
+ // '/(src|background)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui',
+ '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
+ '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
+ '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
+ '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
+ '/<script.+<\/script>/Umis');
+
+ $remote_replaces = array('', // '\\1=\\2#\\4',
+ // '\\1=\\2#\\4',
+ '',
+ '', // '\\1#\\3',
+ 'none',
+ 'none',
+ '');
+
+ // set flag if message containes remote obejcts that where blocked
+ foreach ($remote_patterns as $pattern)
+ {
+ if (preg_match($pattern, $body))
+ {
+ $REMOTE_OBJECTS = TRUE;
+ break;
+ }
+ }
+
+ $body = preg_replace($remote_patterns, $remote_replaces, $body);
+ }
+
+ return sprintf($block, rep_specialchars_output($body, 'html', '', FALSE));
+ }
+
+ // text/enriched
+ if ($ctype_secondary=='enriched')
+ {
+ $body = enriched_to_html($body);
+ return sprintf($block, rep_specialchars_output($body, 'html'));
+ }
+ else
+ {
+ // make links and email-addresses clickable
+ $convert_patterns = $convert_replaces = $replace_strings = array();
+
+ $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:';
+ $url_chars_within = '\?\.~,!';
+
+ $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
+ $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
+
+ $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
+ $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
+
+ $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
+ $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return $JS_OBJECT_NAME.command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
+
+ $body = wordwrap(trim($body), 80);
+ $body = preg_replace($convert_patterns, $convert_replaces, $body);
+
+ // split body into single lines
+ $a_lines = preg_split('/\r?\n/', $body);
+
+ // colorize quoted parts
+ for($n=0; $n<sizeof($a_lines); $n++)
+ {
+ $line = $a_lines[$n];
+
+ if ($line{2}=='>')
+ $color = 'red';
+ else if ($line{1}=='>')
+ $color = 'green';
+ else if ($line{0}=='>')
+ $color = 'blue';
+ else
+ $color = FALSE;
+
+ $line = rep_specialchars_output($line, 'html', 'replace', FALSE);
+
+ if ($color)
+ $a_lines[$n] = sprintf('<font color="%s">%s</font>', $color, $line);
+ else
+ $a_lines[$n] = $line;
+ }
+
+ // insert the links for urls and mailtos
+ $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
+
+ return sprintf($block, "<pre>\n".$body."\n</pre>");
+ }
+ }
+
+
+
+// add a string to the replacement array and return a replacement string
+function rcmail_str_replacement($str, &$rep)
+ {
+ static $count = 0;
+ $rep[$count] = stripslashes($str);
+ return "##string_replacement{".($count++)."}##";
+ }
+
+
+function rcmail_parse_message($structure, $arg=array(), $recursive=FALSE)
+ {
+ global $IMAP;
+ static $sa_inline_objects = array();
+
+ // arguments are: (bool)$prefer_html, (string)$get_url
+ extract($arg);
+
+ $a_attachments = array();
+ $a_return_parts = array();
+ $out = '';
+
+ $message_ctype_primary = strtolower($structure->ctype_primary);
+ $message_ctype_secondary = strtolower($structure->ctype_secondary);
+
+ // show message headers
+ if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
+ $a_return_parts[] = array('type' => 'headers',
+ 'headers' => $structure->headers);
+
+ // print body if message doesn't have multiple parts
+ if ($message_ctype_primary=='text')
+ {
+ $a_return_parts[] = array('type' => 'content',
+ 'body' => $structure->body,
+ 'ctype_primary' => $message_ctype_primary,
+ 'ctype_secondary' => $message_ctype_secondary,
+ 'parameters' => $structure->ctype_parameters,
+ 'encoding' => $structure->headers['content-transfer-encoding']);
+ }
+
+ // message contains alternative parts
+ else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
+ {
+ // get html/plaintext parts
+ $plain_part = $html_part = $print_part = $related_part = NULL;
+
+ foreach ($structure->parts as $p => $sub_part)
+ {
+ $sub_ctype_primary = strtolower($sub_part->ctype_primary);
+ $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
+
+ // check if sub part is
+ if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
+ $plain_part = $p;
+ else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
+ $html_part = $p;
+ else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
+ $enriched_part = $p;
+ else if ($sub_ctype_primary=='multipart' && $sub_ctype_secondary=='related')
+ $related_part = $p;
+ }
+
+ // parse related part (alternative part could be in here)
+ if ($related_part!==NULL && $prefer_html)
+ {
+ list($parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
+ $a_return_parts = array_merge($a_return_parts, $parts);
+ $a_attachments = array_merge($a_attachments, $attachmnts);
+ }
+
+ // print html/plain part
+ else if ($html_part!==NULL && $prefer_html)
+ $print_part = $structure->parts[$html_part];
+ else if ($enriched_part!==NULL)
+ $print_part = $structure->parts[$enriched_part];
+ else if ($plain_part!==NULL)
+ $print_part = $structure->parts[$plain_part];
+
+ // show message body
+ if (is_object($print_part))
+ $a_return_parts[] = array('type' => 'content',
+ 'body' => $print_part->body,
+ 'ctype_primary' => strtolower($print_part->ctype_primary),
+ 'ctype_secondary' => strtolower($print_part->ctype_secondary),
+ 'parameters' => $print_part->ctype_parameters,
+ 'encoding' => $print_part->headers['content-transfer-encoding']);
+ // show plaintext warning
+ else if ($html_part!==NULL)
+ $a_return_parts[] = array('type' => 'content',
+ 'body' => rcube_label('htmlmessage'),
+ 'ctype_primary' => 'text',
+ 'ctype_secondary' => 'plain');
+
+ // add html part as attachment
+ if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
+ {
+ $html_part = $structure->parts[$html_part];
+ $a_attachments[] = array('filename' => rcube_label('htmlmessage'),
+ 'encoding' => $html_part->headers['content-transfer-encoding'],
+ 'mimetype' => 'text/html',
+ 'part_id' => $html_part->mime_id,
+ 'size' => strlen($IMAP->mime_decode($html_part->body, $html_part->headers['content-transfer-encoding'])));
+ }
+ }
+
+ // message contains multiple parts
+ else if ($message_ctype_primary=='multipart' && is_array($structure->parts))
+ {
+ foreach ($structure->parts as $mail_part)
+ {
+ $primary_type = strtolower($mail_part->ctype_primary);
+ $secondary_type = strtolower($mail_part->ctype_secondary);
+
+ // multipart/alternative
+ if ($primary_type=='multipart') // && ($secondary_type=='alternative' || $secondary_type=='mixed' || $secondary_type=='related'))
+ {
+ list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
+
+ $a_return_parts = array_merge($a_return_parts, $parts);
+ $a_attachments = array_merge($a_attachments, $attachmnts);
+ }
+
+ // part text/[plain|html] OR message/delivery-status
+ else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
+ ($primary_type=='message' && $secondary_type=='delivery-status'))
+ {
+ $a_return_parts[] = array('type' => 'content',
+ 'body' => $mail_part->body,
+ 'ctype_primary' => $primary_type,
+ 'ctype_secondary' => $secondary_type,
+ 'parameters' => $mail_part->ctype_parameters,
+ 'encoding' => $mail_part->headers['content-transfer-encoding']);
+ }
+
+ // part message/*
+ else if ($primary_type=='message')
+ {
+ /* don't parse headers here; they're parsed within the recursive call to rcmail_parse_message()
+ if ($mail_part->parts[0]->headers)
+ $a_return_parts[] = array('type' => 'headers',
+ 'headers' => $mail_part->parts[0]->headers);
+ */
+
+ list($parts, $attachmnts) = rcmail_parse_message($mail_part->parts[0], $arg, TRUE);
+
+ $a_return_parts = array_merge($a_return_parts, $parts);
+ $a_attachments = array_merge($a_attachments, $attachmnts);
+ }
+
+ // part is file/attachment
+ else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
+ (empty($mail_part->disposition) && ($mail_part->d_parameters['filename'] || $mail_part->ctype_parameters['name'])))
+ {
+ if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
+ $sa_inline_objects[] = array('filename' => rcube_imap::decode_mime_string($mail_part->d_parameters['filename']),
+ 'mimetype' => strtolower("$primary_type/$secondary_type"),
+ 'part_id' => $mail_part->mime_id,
+ 'content_id' => preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']));
+
+ else if ($mail_part->d_parameters['filename'])
+ $a_attachments[] = array('filename' => rcube_imap::decode_mime_string($mail_part->d_parameters['filename']),
+ 'encoding' => strtolower($mail_part->headers['content-transfer-encoding']),
+ 'mimetype' => strtolower("$primary_type/$secondary_type"),
+ 'part_id' => $mail_part->mime_id,
+ 'size' => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*,
+ 'content' => $mail_part->body */);
+
+ else if ($mail_part->ctype_parameters['name'])
+ $a_attachments[] = array('filename' => rcube_imap::decode_mime_string($mail_part->ctype_parameters['name']),
+ 'encoding' => strtolower($mail_part->headers['content-transfer-encoding']),
+ 'mimetype' => strtolower("$primary_type/$secondary_type"),
+ 'part_id' => $mail_part->mime_id,
+ 'size' => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*,
+ 'content' => $mail_part->body */);
+
+ else if ($mail_part->headers['content-description'])
+ $a_attachments[] = array('filename' => rcube_imap::decode_mime_string($mail_part->headers['content-description']),
+ 'encoding' => strtolower($mail_part->headers['content-transfer-encoding']),
+ 'mimetype' => strtolower("$primary_type/$secondary_type"),
+ 'part_id' => $mail_part->mime_id,
+ 'size' => strlen($IMAP->mime_decode($mail_part->body, $mail_part->headers['content-transfer-encoding'])) /*,
+ 'content' => $mail_part->body */);
+ }
+ }
+
+
+ // if this was a related part try to resolve references
+ if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
+ {
+ $a_replace_patters = array();
+ $a_replace_strings = array();
+
+ foreach ($sa_inline_objects as $inline_object)
+ {
+ $a_replace_patters[] = 'cid:'.$inline_object['content_id'];
+ $a_replace_strings[] = sprintf($get_url, $inline_object['part_id']);
+ }
+
+ foreach ($a_return_parts as $i => $return_part)
+ {
+ if ($return_part['type']!='content')
+ continue;
+
+ // decode body and replace cid:...
+ $a_return_parts[$i]['body'] = str_replace($a_replace_patters, $a_replace_strings, $IMAP->mime_decode($return_part['body'], $return_part['encoding']));
+ $a_return_parts[$i]['encoding'] = '7bit';
+ }
+ }
+ }
+
+
+ // join all parts together
+ //$out .= join($part_delimiter, $a_return_parts);
+
+ return array($a_return_parts, $a_attachments);
+ }
+
+
+
+
+// return table with message headers
+function rcmail_message_headers($attrib, $headers=NULL)
+ {
+ global $IMAP, $OUTPUT, $MESSAGE;
+ static $sa_attrib;
+
+ // keep header table attrib
+ if (is_array($attrib) && !$sa_attrib)
+ $sa_attrib = $attrib;
+ else if (!is_array($attrib) && is_array($sa_attrib))
+ $attrib = $sa_attrib;
+
+
+ if (!isset($MESSAGE))
+ return FALSE;
+
+ // get associative array of headers object
+ if (!$headers)
+ $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
+
+ $header_count = 0;
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+ $out = '<table' . $attrib_str . ">\n";
+
+ // show these headers
+ $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
+
+ foreach ($standard_headers as $hkey)
+ {
+ if (!$headers[$hkey])
+ continue;
+
+ if ($hkey=='date' && !empty($headers[$hkey]))
+ $header_value = format_date(strtotime($headers[$hkey]));
+ else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
+ $header_value = rep_specialchars_output(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']));
+ else
+ $header_value = rep_specialchars_output($IMAP->decode_header($headers[$hkey]), '', 'all');
+
+ $out .= "\n<tr>\n";
+ $out .= '<td class="header-title">'.rep_specialchars_output(rcube_label($hkey)).": </td>\n";
+ $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
+ $header_count++;
+ }
+
+ $out .= "\n</table>\n\n";
+
+ return $header_count ? $out : '';
+ }
+
+
+
+function rcmail_message_body($attrib)
+ {
+ global $CONFIG, $OUTPUT, $MESSAGE, $GET_URL, $REMOTE_OBJECTS, $JS_OBJECT_NAME;
+
+ if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
+ return '';
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmailMsgBody';
+
+ $safe_mode = (bool)$_GET['_safe'];
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+ $out = '<div '. $attrib_str . ">\n";
+
+ $header_attrib = array();
+ foreach ($attrib as $attr => $value)
+ if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
+ $header_attrib[$regs[1]] = $value;
+
+
+ // this is an ecrypted message
+ // -> create a plaintext body with the according message
+ if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
+ {
+ $MESSAGE['parts'][0] = array('type' => 'content',
+ 'ctype_primary' => 'text',
+ 'ctype_secondary' => 'plain',
+ 'body' => rcube_label('encryptedmessage'));
+ }
+
+ if ($MESSAGE['parts'])
+ {
+ foreach ($MESSAGE['parts'] as $i => $part)
+ {
+ if ($part['type']=='headers')
+ $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part['headers']);
+ else if ($part['type']=='content')
+ {
+ if (empty($part['parameters']) || empty($part['parameters']['charset']))
+ $part['parameters']['charset'] = $MESSAGE['headers']->charset;
+
+ // $body = rcmail_print_body($part['body'], $part['ctype_primary'], $part['ctype_secondary'], $part['encoding'], $safe_mode);
+ $body = rcmail_print_body($part, $safe_mode);
+ $out .= '<div class="message-part">';
+
+ if ($part['ctype_secondary']!='plain')
+ $out .= rcmail_mod_html_body($body, $attrib['id']);
+ else
+ $out .= $body;
+
+ $out .= "</div>\n";
+ }
+ }
+ }
+ else
+ $out .= $MESSAGE['body'];
+
+
+ $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
+ $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
+
+ // list images after mail body
+ if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' && $ctype_secondary=='mixed' &&
+ sizeof($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
+ {
+ foreach ($MESSAGE['attachments'] as $attach_prop)
+ {
+ if (strpos($attach_prop['mimetype'], 'image/')===0)
+ $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
+ $GET_URL, $attach_prop['part_id'],
+ $attach_prop['filename'],
+ $attach_prop['filename']);
+ }
+ }
+
+ // tell client that there are blocked remote objects
+ if ($REMOTE_OBJECTS && !$safe_mode)
+ $OUTPUT->add_script(sprintf("%s.set_env('blockedobjects', true);", $JS_OBJECT_NAME));
+
+ $out .= "\n</div>";
+ return $out;
+ }
+
+
+
+// modify a HTML message that it can be displayed inside a HTML page
+function rcmail_mod_html_body($body, $container_id)
+ {
+ // remove any null-byte characters before parsing
+ $body = preg_replace('/\x00/', '', $body);
+
+ $last_style_pos = 0;
+ $body_lc = strtolower($body);
+
+ // find STYLE tags
+ while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
+ {
+ $pos2 += 8;
+ $body_pre = substr($body, 0, $pos);
+ $styles = substr($body, $pos, $pos2-$pos);
+ $body_post = substr($body, $pos2, strlen($body)-$pos2);
+
+ // replace all css definitions with #container [def]
+ $styles = rcmail_mod_css_styles($styles, $container_id);
+
+ $body = $body_pre . $styles . $body_post;
+ $last_style_pos = $pos2;
+ }
+
+
+ // remove SCRIPT tags
+ foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
+ {
+ while (($pos = strpos($body_lc, '<'.$tag)) && ($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)))
+ {
+ $pos2 += 8;
+ $body = substr($body, 0, $pos) . substr($body, $pos2, strlen($body)-$pos2);
+ $body_lc = strtolower($body);
+ }
+ }
+
+ // replace event handlers on any object
+ while ($body != $prev_body)
+ {
+ $prev_body = $body;
+ $body = preg_replace('/(<[^!][^>]*\s)(on[^=>]+)=([^>]+>)/im', '$1__removed=$3', $body);
+ $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
+ }
+
+ // resolve <base href>
+ $base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i';
+ if (preg_match($base_reg, $body, $regs))
+ {
+ $base_url = $regs[2];
+ $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
+ $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
+ $body = preg_replace($base_reg, '', $body);
+ }
+
+ // modify HTML links to open a new window if clicked
+ $body = preg_replace('/<a\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1');", $body);
+
+ // add comments arround html and other tags
+ $out = preg_replace(array('/(<\/?html[^>]*>)/i',
+ '/(<\/?head[^>]*>)/i',
+ '/(<title[^>]*>.*<\/title>)/Ui',
+ '/(<\/?meta[^>]*>)/i'),
+ '<!--\\1-->',
+ $body);
+
+ $out = preg_replace(array('/(<body[^>]*>)/i',
+ '/(<\/body>)/i'),
+ array('<div class="rcmBody">',
+ '</div>'),
+ $out);
+
+ return $out;
+ }
+
+
+// parse link attributes and set correct target
+function rcmail_alter_html_link($in)
+ {
+ $attrib = parse_attrib_string($in);
+
+ if (stristr((string)$attrib['href'], 'mailto:'))
+ $attrib['onclick'] = sprintf("return %s.command('compose','%s',this)",
+ $GLOBALS['JS_OBJECT_NAME'],
+ preg_replace("/'+/i","",substr($attrib['href'], 7)));
+ else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
+ $attrib['target'] = '_blank';
+
+ return '<a' . create_attrib_string($attrib, array('href', 'name', 'target', 'onclick', 'id', 'class', 'style', 'title')) . '>';
+ }
+
+
+// replace all css definitions with #container [def]
+function rcmail_mod_css_styles($source, $container_id)
+ {
+ $a_css_values = array();
+ $last_pos = 0;
+
+ // cut out all contents between { and }
+ while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
+ {
+ $key = sizeof($a_css_values);
+ $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
+ $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
+ $last_pos = $pos+2;
+ }
+
+ $styles = preg_replace('/(^\s*|,\s*)([a-z0-9\._][a-z0-9\.\-_]*)/im', "\\1#$container_id \\2", $source);
+ $styles = preg_replace('/<<str_replacement\[([0-9]+)\]>>/e', "\$a_css_values[\\1]", $styles);
+
+ // replace body definition because we also stripped off the <body> tag
+ $styles = preg_replace("/$container_id\s+body/i", "$container_id div.rcmBody", $styles);
+
+ return $styles;
+ }
+
+
+
+// return first text part of a message
+function rcmail_first_text_part($message_parts)
+ {
+ if (!is_array($message_parts))
+ return FALSE;
+
+ $html_part = NULL;
+
+ // check all message parts
+ foreach ($message_parts as $pid => $part)
+ {
+ $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
+ if ($mimetype=='text/plain')
+ {
+ $body = rcube_imap::mime_decode($part->body, $part->headers['content-transfer-encoding']);
+ $body = rcube_imap::charset_decode($body, $part->ctype_parameters);
+ return $body;
+ }
+ else if ($mimetype=='text/html')
+ {
+ $html_part = rcube_imap::mime_decode($part->body, $part->headers['content-transfer-encoding']);
+ $html_part = rcube_imap::charset_decode($html_part, $part->ctype_parameters);
+ }
+ }
+
+
+ // convert HTML to plain text
+ if ($html_part)
+ {
+ // remove special chars encoding
+ $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
+ $html_part = strtr($html_part, $trans);
+
+ // create instance of html2text class
+ $txt = new html2text($html_part);
+ return $txt->get_text();
+ }
+
+ return FALSE;
+ }
+
+
+// get source code of a specific message and cache it
+function rcmail_message_source($uid)
+ {
+ global $IMAP, $DB, $CONFIG;
+
+ // get message ID if uid is given
+ $cache_key = $IMAP->mailbox.'.msg';
+ $cached = $IMAP->get_cached_message($cache_key, $uid, FALSE);
+
+ // message is cached in database
+ if ($cached && !empty($cached->body))
+ return $cached->body;
+
+ if (!$cached)
+ $headers = $IMAP->get_headers($uid);
+ else
+ $headers = &$cached;
+
+ // create unique identifier based on message_id
+ if (!empty($headers->messageID))
+ $message_id = md5($headers->messageID);
+ else
+ $message_id = md5($headers->uid.'@'.$_SESSION['imap_host']);
+
+ $temp_dir = $CONFIG['temp_dir'].(!eregi('\/$', $CONFIG['temp_dir']) ? '/' : '');
+ $cache_dir = $temp_dir.$_SESSION['client_id'];
+ $cache_path = $cache_dir.'/'.$message_id;
+
+ // message is cached in temp dir
+ if ($CONFIG['enable_caching'] && is_dir($cache_dir) && is_file($cache_path))
+ {
+ if ($fp = fopen($cache_path, 'r'))
+ {
+ $msg_source = fread($fp, filesize($cache_path));
+ fclose($fp);
+ return $msg_source;
+ }
+ }
+
+
+ // get message from server
+ $msg_source = $IMAP->get_raw_body($uid);
+
+ // return message source without caching
+ if (!$CONFIG['enable_caching'])
+ return $msg_source;
+
+
+ // let's cache the message body within the database
+ if ($cached && ($CONFIG['db_max_length'] -300) > $headers->size)
+ {
+ $DB->query("UPDATE ".get_table_name('messages')."
+ SET body=?
+ WHERE user_id=?
+ AND cache_key=?
+ AND uid=?",
+ $msg_source,
+ $_SESSION['user_id'],
+ $cache_key,
+ $uid);
+
+ return $msg_source;
+ }
+
+
+ // create dir for caching
+ if (!is_dir($cache_dir))
+ $dir = mkdir($cache_dir);
+ else
+ $dir = true;
+
+ // attempt to write a file with the message body
+ if ($dir && ($fp = fopen($cache_path, 'w')))
+ {
+ fwrite($fp, $msg_source);
+ fclose($fp);
+ }
+ else
+ {
+ raise_error(array('code' => 403, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => "Failed to write to temp dir"), TRUE, FALSE);
+ }
+
+ return $msg_source;
+ }
+
+
+// decode address string and re-format it as HTML links
+function rcmail_address_string($input, $max=NULL, $addicon=NULL)
+ {
+ global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $EMAIL_ADDRESS_PATTERN;
+
+ $a_parts = $IMAP->decode_address_list($input);
+
+ if (!sizeof($a_parts))
+ return $input;
+
+ $c = count($a_parts);
+ $j = 0;
+ $out = '';
+
+ foreach ($a_parts as $part)
+ {
+ $j++;
+ if ($PRINT_MODE)
+ $out .= sprintf('%s <%s>', rep_specialchars_output($part['name']), $part['mailto']);
+ else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
+ {
+ $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
+ $part['mailto'],
+ $JS_OBJECT_NAME,
+ $part['mailto'],
+ $part['mailto'],
+ rep_specialchars_output($part['name']));
+
+ if ($addicon)
+ $out .= sprintf(' <a href="#add" onclick="return %s.command(\'add-contact\',\'%s\',this)" title="%s"><img src="%s%s" alt="add" border="0" /></a>',
+ $JS_OBJECT_NAME,
+ urlencode($part['string']),
+ rcube_label('addtoaddressbook'),
+ $CONFIG['skin_path'],
+ $addicon);
+ }
+ else
+ {
+ if ($part['name'])
+ $out .= rep_specialchars_output($part['name']);
+ if ($part['mailto'])
+ $out .= (strlen($out) ? ' ' : '') . sprintf('<%s>', $part['mailto']);
+ }
+
+ if ($c>$j)
+ $out .= ','.($max ? ' ' : ' ');
+
+ if ($max && $j==$max && $c>$j)
+ {
+ $out .= '...';
+ break;
+ }
+ }
+
+ return $out;
+ }
+
+
+function rcmail_message_part_controls()
+ {
+ global $CONFIG, $IMAP, $MESSAGE;
+
+ if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$_GET['_part']])
+ return '';
+
+ $part = $MESSAGE['parts'][$_GET['_part']];
+
+ $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
+ $out = '<table '. $attrib_str . ">\n";
+
+ $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name'];
+ $filesize = strlen($IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding']));
+
+ if ($filename)
+ {
+ $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
+ rcube_label('filename'),
+ rep_specialchars_output($filename),
+ str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
+ rcube_label('download'));
+ }
+
+ if ($filesize)
+ $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
+ rcube_label('filesize'),
+ show_bytes($filesize));
+
+ $out .= "\n</table>";
+
+ return $out;
+ }
+
+
+
+function rcmail_message_part_frame($attrib)
+ {
+ global $MESSAGE;
+
+ $part = $MESSAGE['parts'][$_GET['_part']];
+ $ctype_primary = strtolower($part->ctype_primary);
+
+ $attrib['src'] = './?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
+
+ $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
+ $out = '<iframe '. $attrib_str . "></ifame>";
+
+ return $out;
+ }
+
+
+// create temp dir for attachments
+function rcmail_create_compose_tempdir()
+ {
+ global $CONFIG;
+
+ if ($_SESSION['compose']['temp_dir'])
+ return $_SESSION['compose']['temp_dir'];
+
+ if (!empty($CONFIG['temp_dir']))
+ $temp_dir = $CONFIG['temp_dir'].(!eregi('\/$', $CONFIG['temp_dir']) ? '/' : '').$_SESSION['compose']['id'];
+
+ // create temp-dir for uploaded attachments
+ if (!empty($CONFIG['temp_dir']) && is_writeable($CONFIG['temp_dir']))
+ {
+ mkdir($temp_dir);
+ $_SESSION['compose']['temp_dir'] = $temp_dir;
+ }
+
+ return $_SESSION['compose']['temp_dir'];
+ }
+
+
+// clear message composing settings
+function rcmail_compose_cleanup()
+ {
+ if (!isset($_SESSION['compose']))
+ return;
+
+ // remove attachment files from temp dir
+ if (is_array($_SESSION['compose']['attachments']))
+ foreach ($_SESSION['compose']['attachments'] as $attachment)
+ @unlink($attachment['path']);
+
+ // kill temp dir
+ if ($_SESSION['compose']['temp_dir'])
+ @rmdir($_SESSION['compose']['temp_dir']);
+
+ unset($_SESSION['compose']);
+ }
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/get.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Delivering a specific part of a mail message |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: get.inc 147 2006-02-20 23:29:14Z roundcube $
+
+*/
+
+require_once('Mail/mimeDecode.php');
+
+
+// show loading page
+if ($_GET['_preload'])
+ {
+ $url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']);
+ $message = rcube_label('loadingdata');
+
+ print "<html>\n<head>\n" .
+ '<meta http-equiv="refresh" content="0; url='.$url.'">' .
+ "\n</head>\n<body>" .
+ $message .
+ "\n</body>\n</html>";
+ exit;
+ }
+
+
+
+// similar code as in program/steps/mail/show.inc
+if ($_GET['_uid'])
+ {
+ $MESSAGE = array();
+ $MESSAGE['source'] = rcmail_message_source($_GET['_uid']);
+
+ $mmd = new Mail_mimeDecode($MESSAGE['source']);
+ $MESSAGE['structure'] = $mmd->decode(array('include_bodies' => TRUE,
+ 'decode_headers' => FALSE,
+ 'decode_bodies' => FALSE));
+
+ $MESSAGE['parts'] = $mmd->getMimeNumbers($MESSAGE['structure']);
+ }
+
+
+
+// show part page
+if ($_GET['_frame'])
+ {
+ parse_template('messagepart');
+ exit;
+ }
+
+else if ($_GET['_part'])
+ {
+ if ($part = $MESSAGE['parts'][$_GET['_part']]);
+ {
+ $ctype_primary = strtolower($part->ctype_primary);
+ $ctype_secondary = strtolower($part->ctype_secondary);
+
+ $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary);
+ $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name'];
+
+ if ($ctype_primary=='text' && $ctype_secondary=='html')
+ {
+ list($MESSAGE['parts']) = rcmail_parse_message($part,
+ array('safe' => (bool)$_GET['_safe'],
+ 'prefer_html' => TRUE,
+ 'get_url' => $GET_URL.'&_part=%s'));
+
+ $cont = rcmail_print_body($MESSAGE['parts'][0], (bool)$_GET['_safe']);
+ }
+ else
+ $cont = $IMAP->mime_decode($part->body, $part->headers['content-transfer-encoding']);
+
+ // send correct headers for content type and length
+ if ($_GET['_download'])
+ {
+ // send download headers
+ header("Content-Type: application/octet-stream");
+ header(sprintf('Content-Disposition: attachment; filename="%s"',
+ $filename ? $filename : "roundcube.$ctype_secondary"));
+ }
+ else
+ {
+ header("Content-Type: $mimetype");
+ header(sprintf('Content-Disposition: inline; filename="%s"', $filename));
+ }
+
+ header(sprintf('Content-Length: %d', strlen($cont)));
+
+ // We need to set the following headers to make downloads work using IE in HTTPS mode.
+ if (isset($_SERVER['HTTPS']))
+ {
+ header('Pragma: ');
+ header('Cache-Control: ');
+ }
+
+ // deliver part content
+ echo $cont;
+ exit;
+ }
+ }
+
+// print message
+else
+ {
+ $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
+ $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
+ $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary);
+
+ // send correct headers for content type
+ header("Content-Type: text/html");
+
+ $cont = '';
+ list($MESSAGE['parts']) = rcmail_parse_message($MESSAGE['structure'],
+ array('safe' => (bool)$_GET['_safe'],
+ 'get_url' => $GET_URL.'&_part=%s'));
+
+ if ($MESSAGE['parts'] && $ctype_primary=='multipart')
+ {
+ // reset output page
+ $OUTPUT = new rcube_html_page();
+ parse_template('messagepart');
+ exit;
+ }
+ else if ($MESSAGE['parts'][0])
+ {
+ $part = $MESSAGE['parts'][0];
+ $cont = rcmail_print_body($part, (bool)$_GET['_safe']);
+ }
+ else
+ $cont = $IMAP->get_body($_GET['_uid']);
+
+ $OUTPUT = new rcube_html_page();
+ $OUTPUT->write($cont);
+
+/*
+ if ($mimetype=='text/html')
+ print $cont;
+ else
+ {
+ print "<html>\n<body>\n";
+ print $cont;
+ print "\n</body>\n</html>";
+ }
+*/
+ exit;
+ }
+
+
+// if we arrive here, the requested part was not found
+header('HTTP/1.1 404 Not Found');
+exit;
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/getunread.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Check all mailboxes for unread messages and update GUI |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: getunread.inc 269 2006-06-29 23:41:40Z richs $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+$a_folders = $IMAP->list_mailboxes();
+
+if (!empty($a_folders))
+ {
+ foreach ($a_folders as $mbox_row)
+ {
+ $commands = sprintf("this.set_unread_count('%s', %d);\n", addslashes($mbox_row), $IMAP->messagecount($mbox_row, 'UNSEEN'));
+ rcube_remote_response($commands, TRUE);
+ }
+ }
+
+exit;
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/list.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Send message list to client (as remote response) |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: list.inc 232 2006-05-18 15:46:50Z cmcnulty $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+$OUTPUT_TYPE = 'js';
+
+$sort = isset($_GET['_sort']) ? $_GET['_sort'] : false;
+
+// is there a sort type for this request?
+if ($sort)
+ {
+ // yes, so set the sort vars
+ list($sort_col, $sort_order) = explode('_', $sort);
+
+ // set session vars for sort (so next page and task switch know how to sort)
+ $_SESSION['sort_col'] = $sort_col;
+ $_SESSION['sort_order'] = $sort_order;
+ }
+else
+ {
+ // use session settings if set, defaults if not
+ $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col'];
+ $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order'];
+ }
+
+
+// we have a saved search request
+if (!empty($_GET['_search']) && isset($_SESSION['search'][$_GET['_search']]))
+ {
+ $a_msgs = split(',', $_SESSION['search'][$_GET['_search']]);
+ $a_headers = $IMAP->list_header_set($mbox_name, $a_msgs, NULL, $sort_col, $sort_order);
+ $count = count($a_msgs);
+ }
+else
+ {
+ if ($count = $IMAP->messagecount())
+ $a_headers = $IMAP->list_headers($mbox_name, NULL, $sort_col, $sort_order);
+ }
+
+$unseen = $IMAP->messagecount($mbox_name, 'UNSEEN', !empty($_GET['_refresh']) ? TRUE : FALSE);
+
+// update message count display
+$pages = ceil($count/$IMAP->page_size);
+$commands = sprintf("this.set_env('messagecount', %d);\n", $count);
+$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+$commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text($count));
+
+// update mailboxlist
+$mbox_name = $IMAP->get_mailbox_name();
+$commands .= sprintf("this.set_unread_count('%s', %d);\n", addslashes($mbox_name), $unseen);
+
+
+// add message rows
+if (isset($a_headers) && count($a_headers))
+ $commands .= rcmail_js_message_list($a_headers);
+
+
+// send response
+rcube_remote_response($commands);
+
+exit;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/mark.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Mark the submitted messages with the specified flag |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: mark.inc 232 2006-05-18 15:46:50Z cmcnulty $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+$a_flags_map = array('undelete' => 'UNDELETED',
+ 'delete' => 'DELETED',
+ 'read' => 'SEEN',
+ 'unread' => 'UNSEEN');
+
+if ($_GET['_uid'] && $_GET['_flag'])
+ {
+ $flag = $a_flags_map[$_GET['_flag']] ? $a_flags_map[$_GET['_flag']] : strtoupper($_GET['_flag']);
+ $marked = $IMAP->set_flag($_GET['_uid'], $flag);
+ if ($marked != -1)
+ {
+ $mbox_name = $IMAP->get_mailbox_name();
+ $commands = sprintf("this.set_unread_count('%s', %d);\n", $mbox_name, $IMAP->messagecount($mbox_name, 'UNSEEN'));
+ rcube_remote_response($commands);
+ }
+ }
+
+exit;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/move_del.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Move the submitted messages to a specific mailbox or delete them |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: move_del.inc 269 2006-06-29 23:41:40Z richs $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+// move messages
+if ($_action=='moveto' && $_GET['_uid'] && $_GET['_target_mbox'])
+ {
+ $count = sizeof(explode(',', $_GET['_uid']));
+ $moved = $IMAP->move_message($_GET['_uid'], $_GET['_target_mbox'], $_GET['_mbox']);
+
+ if (!$moved)
+ {
+ // send error message
+ $commands = "this.list_mailbox();\n";
+ $commands .= show_message('errormoving', 'error');
+ rcube_remote_response($commands);
+ exit;
+ }
+ }
+
+// delete messages
+else if ($_action=='delete' && $_GET['_uid'])
+ {
+ $count = sizeof(explode(',', $_GET['_uid']));
+ $del = $IMAP->delete_message($_GET['_uid'], $_GET['_mbox']);
+
+ if (!$del)
+ {
+ // send error message
+ $commands = "this.list_mailbox();\n";
+ $commands .= show_message('errordeleting', 'error');
+ rcube_remote_response($commands);
+ exit;
+ }
+ }
+
+// unknown action or missing query param
+else
+ {
+ exit;
+ }
+
+
+// update message count display
+$pages = ceil($IMAP->messagecount()/$IMAP->page_size);
+$commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text());
+$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+
+
+// update mailboxlist
+$mbox = $IMAP->get_mailbox_name();
+$commands .= sprintf("this.set_unread_count('%s', %d);\n", $mbox, $IMAP->messagecount($mbox, 'UNSEEN'));
+
+if ($_action=='moveto')
+ $commands .= sprintf("this.set_unread_count('%s', %d);\n", $_GET['_target_mbox'], $IMAP->messagecount($_GET['_target_mbox'], 'UNSEEN'));
+
+$commands .= sprintf("this.set_quota('%s');\n", $IMAP->get_quota());
+
+// add new rows from next page (if any)
+if ($_GET['_from']!='show' && $pages>1 && $IMAP->list_page < $pages)
+ {
+ $a_headers = $IMAP->list_headers($mbox, null, $_SESSION['sort_col'], $_SESSION['sort_order']);
+ $a_headers = array_slice($a_headers, -$count, $count);
+ $commands .= rcmail_js_message_list($a_headers);
+ }
+
+
+// send response
+rcube_remote_response($commands);
+
+exit;
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/rss.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Send mailboxcontents as RSS feed |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Sjon Hortensius <sjon@hortensius.net> |
+ +-----------------------------------------------------------------------+
+
+ $Id: rss.inc 134 2006-02-05 16:35:40Z roundcube $
+
+*/
+
+require_once('Mail/mimeDecode.php');
+
+
+function rss_encode($string){
+ $string = rep_specialchars_output($string, 'xml');
+ return $string;
+}
+
+
+
+$REMOTE_REQUEST = TRUE;
+$OUTPUT_TYPE = 'rss';
+
+$webmail_url = 'http';
+if (strstr('HTTPS', $_SERVER['SERVER_PROTOCOL'] )!== FALSE)
+ $webmail_url .= 's';
+$webmail_url .= '://'.$_SERVER['SERVER_NAME'];
+if ($_SERVER['SERVER_PORT'] != '80')
+ $webmail_url .= ':'.$_SERVER['SERVER_PORT'];
+$webmail_url .= '/';
+if (dirname($_SERVER['SCRIPT_NAME']) != '/')
+ $webmail_url .= dirname($_SERVER['SCRIPT_NAME']).'/';
+
+$auth_webmail_url = $webmail_url.'?_auth='.$GLOBALS['sess_auth'];
+
+$messagecount_unread = $IMAP->messagecount('INBOX', 'UNSEEN', TRUE);
+$messagecount = $IMAP->messagecount();
+
+$sort_col = 'date';
+$sort_order = 'DESC';
+
+// Send global XML output
+header('Content-type: text/xml');
+echo '<?xml version="1.0" encoding="UTF-8"?>
+ <rss version="2.0"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
+ xmlns:admin="http://webns.net/mvcb/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/">';
+
+// Send channel-specific output
+echo '
+ <channel>
+ <pubDate>'.date('r').'</pubDate>
+ <lastBuildDate>'.date('r').'</lastBuildDate>
+ <ttl>5</ttl>
+ <docs>http://blogs.law.harvard.edu/tech/rss</docs>
+ <description>INBOX contains '.$messagecount.' messages, of which '.$messagecount_unread.' unread</description>
+ <link>'.rss_encode($auth_webmail_url, 'xml') .'</link>
+ <title>webmail for '.rss_encode($_SESSION['username'].' @ '.$_SESSION['imap_host']).'</title>
+ <generator>'.rss_encode($CONFIG['useragent'], 'xml').' (RSS extension by Sjon Hortensius)</generator>
+ <image>
+ <link>http://www.roundcube.net/</link>
+ <title>'.rss_encode($CONFIG['product_name']).' logo</title>
+ <url>'.rss_encode($webmail_url.'skins/default/images/roundcube_logo.png').'</url>
+ </image>';
+
+// Check if the user wants to override the default sortingmethode
+if (isset($_GET['_sort']))
+ list($sort_col, $sort_order) = explode('_', $_GET['_sort']);
+
+// Add message to output
+if ($messagecount > 0)
+ {
+ $items = $IMAP->list_headers('INBOX', null, $sort_col, $sort_order);
+ foreach ($items as $item)
+ {
+
+ // Convert '"name" <email>' to 'email (name)'
+ if (strstr($item->from, '<'))
+ $item->from = preg_replace('~"?([^"]*)"? <([^>]*)>~', '\2 (\1)', $item->from);
+
+ $item->link = $auth_webmail_url.'&_task=mail&_action=show&_uid='.$item->uid.'&_mbox=INBOX';
+
+ $item->body = $IMAP->get_body($item->uid);
+
+ // Print the actual messages
+ echo '
+ <item>
+ <title>'.rss_encode($item->subject).'</title>
+ <link>'.rss_encode($item->link).'</link>
+ <description><![CDATA['."\n".nl2br(rss_encode($item->body))."\n".']]></description>
+ <author>'.rss_encode($item->from).'</author>
+ <category></category>
+ <guid>'.rss_encode($item->link).'</guid>
+ <pubDate>'.date('r', $item->timestamp).'</pubDate>
+ </item>';
+ }
+ }
+
+echo '</channel>
+</rss>';
+
+exit;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/*
+ +-----------------------------------------------------------------------+
+ | steps/mail/search.inc |
+ | |
+ | Search functions for rc webmail |
+ | Licensed under the GNU GPL |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Benjamin Smith <defitro@gmail.com> |
+ | Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+// reset list_page
+$IMAP->set_page(1);
+$_SESSION['page'] = 1;
+
+// search query comes in with ISO encoding because javascript escape()
+// uses ISO-8859-1. Better handling for that will follow.
+$imap_charset = 'ISO-8859-1';
+
+// get search string
+$str = get_input_value('_search', RCUBE_INPUT_GET);
+$mbox = get_input_value('_mbox', RCUBE_INPUT_GET);
+$search_request = md5($str);
+
+
+// Check the search string for type of search
+if (preg_match("/^from:/i", $str)) {
+ list(,$srch) = explode(":", $str);
+ $search = $IMAP->search($mbox, "HEADER FROM" ,trim($srch), $imap_charset);
+ finish_search($mbox, $search);
+}
+else if (preg_match("/^to:/i", $str)) {
+ list(,$srch) = explode(":", $str);
+ $search = $IMAP->search($mbox, "HEADER TO", trim($srch), $imap_charset);
+ finish_search($mbox, $search);
+}
+else if (preg_match("/^cc:/i", $str)) {
+ list(,$srch) = explode(":", $str);
+ $search = $IMAP->search($mbox, "HEADER CC", trim($srch), $imap_charset);
+ finish_search($mbox, $search);
+}
+else if (preg_match("/^subject:/i", $str)) {
+ list(,$srch) = explode(":", $str);
+ $search = $IMAP->search($mbox, "HEADER SUBJECT", trim($srch), $imap_charset);
+ finish_search($mbox, $search);
+}
+else if (preg_match("/^body:/i", $str)) {
+ list(,$srch) = explode(":", $str);
+ $search = $IMAP->search($mbox, "TEXT", trim($srch), $imap_charset);
+ finish_search($mbox, $search);
+}
+// search in subject and sender by default
+else {
+ $search = $IMAP->search($mbox, "HEADER SUBJECT", trim($str), $imap_charset);
+ $search2 = $IMAP->search($mbox, "HEADER FROM", trim($str), $imap_charset);
+ finish_search($mbox, array_unique(array_merge($search, $search2)));
+}
+
+
+// Complete the search display results or report error
+function finish_search($mbox, $search)
+ {
+ global $IMAP, $JS_OBJECT_NAME, $OUTPUT, $search_request;
+ $commands = '';
+ $count = 0;
+
+ // Make sure our $search is legit..
+ if (is_array($search) && $search[0] != '')
+ {
+ // Get the headers
+ $result_h = $IMAP->list_header_set($mbox, $search, 1, $_SESSION['sort_col'], $_SESSION['sort_order']);
+ $count = count($search);
+
+ // save search results in session
+ if (!is_array($_SESSION['search']))
+ $_SESSION['search'] = array();
+
+ // Make sure we got the headers
+ if ($result_h != NULL)
+ {
+ $_SESSION['search'][$search_request] = join(',', $search);
+ $commands = rcmail_js_message_list($result_h);
+ $commands .= show_message('searchsuccessful', 'confirmation', array('nr' => $count));
+ }
+ }
+ else
+ {
+ $commands = show_message('searchnomatch', 'warning');
+ $search_request = -1;
+ }
+
+ // update message count display
+ $pages = ceil($count/$IMAP->page_size);
+ $commands .= sprintf("\nthis.set_env('search_request', '%s')\n", $search_request);
+ $commands .= sprintf("this.set_env('messagecount', %d);\n", $count);
+ $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+ $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text($count, 1));
+ rcube_remote_response($commands);
+ }
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/sendmail.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Compose a new mail message with all headers and attachments |
+ | and send it using IlohaMail's SMTP methods or with PHP mail() |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: sendmail.inc 288 2006-07-31 22:51:23Z thomasb $
+
+*/
+
+
+//require_once('lib/smtp.inc');
+require_once('include/rcube_smtp.inc');
+require_once('Mail/mime.php');
+
+
+if (!isset($_SESSION['compose']['id']))
+ {
+ rcmail_overwrite_action('list');
+ return;
+ }
+
+
+/****** message sending functions ********/
+
+
+function rcmail_get_identity($id)
+ {
+ global $DB, $CHARSET, $OUTPUT;
+
+ // get identity record
+ $sql_result = $DB->query("SELECT *, email AS mailto
+ FROM ".get_table_name('identities')."
+ WHERE identity_id=?
+ AND user_id=?
+ AND del<>1",
+ $id,$_SESSION['user_id']);
+
+ if ($DB->num_rows($sql_result))
+ {
+ $sql_arr = $DB->fetch_assoc($sql_result);
+ $out = $sql_arr;
+ $name = strpos($sql_arr['name'], ",") ? '"'.$sql_arr['name'].'"' : $sql_arr['name'];
+ $out['string'] = sprintf('%s <%s>',
+ rcube_charset_convert($name, $CHARSET, $OUTPUT->get_charset()),
+ $sql_arr['mailto']);
+ return $out;
+ }
+
+ return FALSE;
+ }
+
+
+if (strlen($_POST['_draft_saveid']) > 3)
+ $olddraftmessageid = get_input_value('_draft_saveid', RCUBE_INPUT_POST);
+
+$message_id = sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), $_SESSION['imap_host']);
+$savedraft = !empty($_POST['_draft']) ? TRUE : FALSE;
+
+// remove all scripts and act as called in frame
+$OUTPUT->reset();
+$_framed = TRUE;
+
+
+/****** check submission and compose message ********/
+
+
+if (empty($_POST['_to']) && empty($_POST['_subject']) && $_POST['_message'])
+ {
+ show_message("sendingfailed", 'error');
+ //rcmail_overwrite_action('compose');
+ rcube_iframe_response();
+ return;
+ }
+
+
+// set default charset
+$input_charset = $OUTPUT->get_charset();
+$message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $input_charset;
+
+$mailto_regexp = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m');
+$mailto_replace = array(', ', ', ', '');
+
+// replace new lines and strip ending ', '
+$mailto = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_to', RCUBE_INPUT_POST, TRUE, $message_charset));
+
+// decode address strings
+$to_address_arr = $IMAP->decode_address_list($mailto);
+$identity_arr = rcmail_get_identity(get_input_value('_from', RCUBE_INPUT_POST));
+
+$from = $identity_arr['mailto'];
+$first_to = is_array($to_address_arr[0]) ? $to_address_arr[0]['mailto'] : $mailto;
+
+if (empty($identity_arr['string']))
+ $identity_arr['string'] = $from;
+
+// compose headers array
+$headers = array('Date' => date('D, j M Y G:i:s O'),
+ 'From' => $identity_arr['string'],
+ 'To' => rcube_charset_convert($mailto, $input_charset, $message_charset));
+
+// additional recipients
+if (!empty($_POST['_cc']))
+ $headers['Cc'] = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_cc', RCUBE_INPUT_POST, TRUE, $message_charset));
+
+if (!empty($_POST['_bcc']))
+ $headers['Bcc'] = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_bcc', RCUBE_INPUT_POST, TRUE, $message_charset));
+
+if (!empty($identity_arr['bcc']))
+ $headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc'];
+
+// add subject
+$headers['Subject'] = trim(get_input_value('_subject', RCUBE_INPUT_POST, FALSE, $message_charset));
+
+if (!empty($identity_arr['organization']))
+ $headers['Organization'] = $identity_arr['organization'];
+
+if (!empty($identity_arr['reply-to']))
+ $headers['Reply-To'] = $identity_arr['reply-to'];
+
+if (!empty($_SESSION['compose']['reply_msgid']))
+ $headers['In-Reply-To'] = $_SESSION['compose']['reply_msgid'];
+
+if (!empty($_SESSION['compose']['references']))
+ $headers['References'] = $_SESSION['compose']['references'];
+
+if (!empty($_POST['_priority']))
+ {
+ $priority = (int)$_POST['_priority'];
+ $a_priorities = array(1=>'lowest', 2=>'low', 4=>'high', 5=>'highest');
+ if ($str_priority = $a_priorities[$priority])
+ $headers['X-Priority'] = sprintf("%d (%s)", $priority, ucfirst($str_priority));
+ }
+
+if (!empty($_POST['_receipt']))
+ {
+ $headers['Return-Receipt-To'] = $identity_arr['string'];
+ $headers['Disposition-Notification-To'] = $identity_arr['string'];
+ }
+
+// additional headers
+$headers['Message-ID'] = $message_id;
+$headers['X-Sender'] = $from;
+
+if (!empty($CONFIG['useragent']))
+ $headers['User-Agent'] = $CONFIG['useragent'];
+
+// fetch message body
+$message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset);
+
+// append generic footer to all messages
+if (!empty($CONFIG['generic_message_footer']))
+ {
+ $file = realpath($CONFIG['generic_message_footer']);
+ if($fp = fopen($file, 'r'))
+ {
+ $content = fread($fp, filesize($file));
+ fclose($fp);
+ $message_body .= "\r\n" . rcube_charset_convert($content, 'UTF-8', $message_charset);
+ }
+ }
+
+// try to autodetect operating system and use the correct line endings
+// use the configured delimiter for headers
+if (!empty($CONFIG['mail_header_delimiter']))
+ $header_delm = $CONFIG['mail_header_delimiter'];
+else if (strtolower(substr(PHP_OS, 0, 3)=='win'))
+ $header_delm = "\r\n";
+else if (strtolower(substr(PHP_OS, 0, 3)=='mac'))
+ $header_delm = "\r\n";
+else
+ $header_delm = "\n";
+
+// create PEAR::Mail_mime instance
+$MAIL_MIME = new Mail_mime($header_delm);
+$MAIL_MIME->setTXTBody($message_body, FALSE, TRUE);
+//$MAIL_MIME->setTXTBody(wordwrap($message_body), FALSE, TRUE);
+
+
+// add stored attachments, if any
+if (is_array($_SESSION['compose']['attachments']))
+ foreach ($_SESSION['compose']['attachments'] as $attachment)
+ $MAIL_MIME->addAttachment($attachment['path'], $attachment['mimetype'], $attachment['name'], TRUE);
+
+
+// add submitted attachments
+if (is_array($_FILES['_attachments']['tmp_name']))
+ foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath)
+ $MAIL_MIME->addAttachment($filepath, $files['type'][$i], $files['name'][$i], TRUE);
+
+
+// chose transfer encoding
+$charset_7bit = array('ASCII', 'ISO-2022-JP', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-15');
+$transfer_encoding = in_array(strtoupper($message_charset), $charset_7bit) ? '7bit' : '8bit';
+
+// encoding settings for mail composing
+$message_param = array('text_encoding' => $transfer_encoding,
+ 'html_encoding' => 'quoted-printable',
+ 'head_encoding' => 'quoted-printable',
+ 'head_charset' => $message_charset,
+ 'html_charset' => $message_charset,
+ 'text_charset' => $message_charset);
+
+// compose message body and get headers
+$msg_body = &$MAIL_MIME->get($message_param);
+
+$msg_subject = $headers['Subject'];
+
+if ($MBSTRING && function_exists("mb_encode_mimeheader"))
+ $headers['Subject'] = mb_encode_mimeheader($headers['Subject'], $message_charset);
+
+// Begin SMTP Delivery Block
+if (!$savedraft) {
+
+ // send thru SMTP server using custom SMTP library
+ if ($CONFIG['smtp_server'])
+ {
+ // generate list of recipients
+ $a_recipients = array($mailto);
+
+ if (strlen($headers['Cc']))
+ $a_recipients[] = $headers['Cc'];
+ if (strlen($headers['Bcc']))
+ $a_recipients[] = $headers['Bcc'];
+
+ // clean Bcc from header for recipients
+ $send_headers = $headers;
+ unset($send_headers['Bcc']);
+
+ // generate message headers
+ $header_str = $MAIL_MIME->txtHeaders($send_headers);
+
+ // send message
+ $sent = smtp_mail($from, $a_recipients, $header_str, $msg_body);
+
+ // log error
+ if (!$sent)
+ {
+ raise_error(array('code' => 800,
+ 'type' => 'smtp',
+ 'line' => __LINE__,
+ 'file' => __FILE__,
+ 'message' => "SMTP error: $SMTP_ERROR"), TRUE, FALSE);
+ }
+ }
+
+ // send mail using PHP's mail() function
+ else
+ {
+ // unset some headers because they will be added by the mail() function
+ $headers_enc = $MAIL_MIME->headers($headers);
+ $headers_php = $MAIL_MIME->_headers;
+ unset($headers_php['To'], $headers_php['Subject']);
+
+ // reset stored headers and overwrite
+ $MAIL_MIME->_headers = array();
+ $header_str = $MAIL_MIME->txtHeaders($headers_php);
+
+ if (ini_get('safe_mode'))
+ $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
+ else
+ $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
+ }
+
+
+ // return to compose page if sending failed
+ if (!$sent)
+ {
+ show_message("sendingfailed", 'error');
+ rcube_iframe_response();
+ return;
+ }
+
+
+ // set repliead flag
+ if ($_SESSION['compose']['reply_uid'])
+ $IMAP->set_flag($_SESSION['compose']['reply_uid'], 'ANSWERED');
+
+ } // End of SMTP Delivery Block
+
+
+
+// Determine which folder to save message
+if ($savedraft)
+ $store_target = 'drafts_mbox';
+else
+ $store_target = 'sent_mbox';
+
+if ($CONFIG[$store_target])
+ {
+ // create string of complete message headers
+ $header_str = $MAIL_MIME->txtHeaders($headers);
+
+ // check if mailbox exists
+ if (!in_array_nocase($CONFIG[$store_target], $IMAP->list_mailboxes()))
+ $store_folder = $IMAP->create_mailbox($CONFIG[$store_target], TRUE);
+ else
+ $store_folder = TRUE;
+
+ // add headers to message body
+ $msg_body = $header_str."\r\n".$msg_body;
+
+ // append message to sent box
+ if ($store_folder)
+ $saved = $IMAP->save_message($CONFIG[$store_target], $msg_body);
+
+ // raise error if saving failed
+ if (!$saved)
+ {
+ raise_error(array('code' => 800,
+ 'type' => 'imap',
+ 'file' => __FILE__,
+ 'message' => "Could not save message in $CONFIG[$store_target]"), TRUE, FALSE);
+
+ show_message('errorsaving', 'error');
+ rcube_iframe_response($errorout);
+ }
+
+ if ($olddraftmessageid)
+ {
+ // delete previous saved draft
+ $a_deleteid = $IMAP->search($CONFIG['drafts_mbox'],'HEADER Message-ID',$olddraftmessageid);
+ $deleted = $IMAP->delete_message($IMAP->get_uid($a_deleteid[0],$CONFIG['drafts_mbox']),$CONFIG['drafts_mbox']);
+
+ // raise error if deletion of old draft failed
+ if (!$deleted)
+ raise_error(array('code' => 800,
+ 'type' => 'imap',
+ 'file' => __FILE__,
+ 'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
+ }
+ }
+
+if ($savedraft)
+ {
+ // clear the "saving message" busy status, and display success
+ show_message('messagesaved', 'confirmation');
+
+ // update "_draft_saveid" on the page, which is used to delete a previous draft
+ $frameout = "var foundid = parent.rcube_find_object('_draft_saveid', parent.document);\n";
+ $frameout .= sprintf("foundid.value = '%s';\n", str_replace(array('<','>'), "", $message_id));
+
+ // update the "cmp_hash" to prevent "Unsaved changes" warning
+ $frameout .= sprintf("parent.%s.cmp_hash = parent.%s.compose_field_hash();\n", $JS_OBJECT_NAME, $JS_OBJECT_NAME);
+
+ // start the auto-save timer again
+ $frameout .= sprintf("parent.%s.auto_save_start();", $JS_OBJECT_NAME);
+
+ // send html page with JS calls as response
+ rcube_iframe_response($frameout);
+ }
+else
+ {
+ if ($CONFIG['smtp_log'])
+ {
+ $log_entry = sprintf("[%s] User: %d on %s; Message for %s; Subject: %s\n",
+ date("d-M-Y H:i:s O", mktime()),
+ $_SESSION['user_id'],
+ $_SERVER['REMOTE_ADDR'],
+ $mailto,
+ $msg_subject);
+
+ if ($fp = @fopen($CONFIG['log_dir'].'/sendmail', 'a'))
+ {
+ fwrite($fp, $log_entry);
+ fclose($fp);
+ }
+ }
+
+ rcmail_compose_cleanup();
+ rcube_iframe_response(sprintf("parent.$JS_OBJECT_NAME.sent_successfully('%s');",
+ rep_specialchars_output(rcube_label('messagesent'), 'js')));
+ }
+
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/show.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Display a mail message similar as a usual mail application does |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: show.inc 277 2006-07-19 19:36:35Z thomasb $
+
+*/
+
+require_once('Mail/mimeDecode.php');
+
+$PRINT_MODE = $_action=='print' ? TRUE : FALSE;
+
+
+// similar code as in program/steps/mail/get.inc
+if ($_GET['_uid'])
+ {
+ $MESSAGE = array();
+ $MESSAGE['headers'] = $IMAP->get_headers($_GET['_uid']);
+ $MESSAGE['source'] = rcmail_message_source($_GET['_uid']);
+
+ // go back to list if message not found (wrong UID)
+ if (!$MESSAGE['headers'] || !$MESSAGE['source'])
+ {
+ $_action = 'list';
+ return;
+ }
+
+ $mmd = new Mail_mimeDecode($MESSAGE['source']);
+ $MESSAGE['structure'] = $mmd->decode(array('include_bodies' => TRUE,
+ 'decode_headers' => FALSE,
+ 'decode_bodies' => FALSE));
+
+ $mmd->getMimeNumbers($MESSAGE['structure']);
+
+ $MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['structure']->headers['subject']);
+
+ if ($MESSAGE['structure'])
+ list($MESSAGE['parts'], $MESSAGE['attachments']) = rcmail_parse_message($MESSAGE['structure'],
+ array('safe' => (bool)$_GET['_safe'],
+ 'prefer_html' => $CONFIG['prefer_html'],
+ 'get_url' => $GET_URL.'&_part=%s'));
+ else
+ $MESSAGE['body'] = $IMAP->get_body($_GET['_uid']);
+
+
+ // mark message as read
+ if (!$MESSAGE['headers']->seen)
+ $IMAP->set_flag($_GET['_uid'], 'SEEN');
+
+ // give message uid to the client
+ $javascript = sprintf("%s.set_env('uid', '%s');\n", $JS_OBJECT_NAME, $_GET['_uid']);
+ $javascript .= sprintf("%s.set_env('safemode', '%b');", $JS_OBJECT_NAME, $_GET['_safe']);
+
+ $next = $prev = -1;
+ // get previous and next message UID
+ if (!($_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] == 'DESC') &&
+ $IMAP->get_capability('sort')) {
+ // Only if we use custom sorting
+ $a_msg_index = $IMAP->message_index(NULL, $_SESSION['sort_col'], $_SESSION['sort_order']);
+
+ $MESSAGE['index'] = array_search((string)$_GET['_uid'], $a_msg_index, TRUE);
+ $prev = isset($a_msg_index[$MESSAGE['index']-1]) ? $a_msg_index[$MESSAGE['index']-1] : -1 ;
+ $next = isset($a_msg_index[$MESSAGE['index']+1]) ? $a_msg_index[$MESSAGE['index']+1] : -1 ;
+ } else {
+ // this assumes that we are sorted by date_DESC
+ $seq = $IMAP->get_id($_GET['_uid']);
+ $prev = $IMAP->get_uid($seq + 1);
+ $next = $IMAP->get_uid($seq - 1);
+ $MESSAGE['index'] = $IMAP->messagecount() - $seq;
+ }
+
+ if ($prev > 0)
+ $javascript .= sprintf("\n%s.set_env('prev_uid', '%s');", $JS_OBJECT_NAME, $prev);
+ if ($next > 0)
+ $javascript .= sprintf("\n%s.set_env('next_uid', '%s');", $JS_OBJECT_NAME, $next);
+
+ $OUTPUT->add_script($javascript);
+ }
+
+
+
+function rcmail_message_attachments($attrib)
+ {
+ global $CONFIG, $OUTPUT, $PRINT_MODE, $MESSAGE, $GET_URL, $JS_OBJECT_NAME;
+
+ if (sizeof($MESSAGE['attachments']))
+ {
+ // allow the following attributes to be added to the <ul> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+ $out = '<ul' . $attrib_str . ">\n";
+
+ foreach ($MESSAGE['attachments'] as $attach_prop)
+ {
+ if ($PRINT_MODE)
+ $out .= sprintf('<li>%s (%s)</li>'."\n",
+ $attach_prop['filename'],
+ show_bytes($attach_prop['size']));
+ else
+ $out .= sprintf('<li><a href="%s&_part=%s" onclick="return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)">%s</a></li>'."\n",
+ htmlentities($GET_URL),
+ $attach_prop['part_id'],
+ $JS_OBJECT_NAME,
+ $attach_prop['part_id'],
+ $attach_prop['mimetype'],
+ $attach_prop['filename']);
+ }
+
+ $out .= "</ul>";
+ return $out;
+ }
+ }
+
+
+
+// return an HTML iframe for loading mail content
+function rcmail_messagecontent_frame($attrib)
+ {
+ global $COMM_PATH, $OUTPUT, $GET_URL, $JS_OBJECT_NAME;
+
+ // allow the following attributes to be added to the <iframe> tag
+ $attrib_str = create_attrib_string($attrib);
+ $framename = 'rcmailcontentwindow';
+
+ $out = sprintf('<iframe src="%s" name="%s"%s>%s</iframe>'."\n",
+ $GET_URL,
+ $framename,
+ $attrib_str,
+ rcube_label('loading'));
+
+
+ $OUTPUT->add_script("$JS_OBJECT_NAME.set_env('contentframe', '$framename');");
+
+ return $out;
+ }
+
+
+function rcmail_remote_objects_msg($attrib)
+ {
+ global $CONFIG, $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmremoteobjmsg';
+
+ // allow the following attributes to be added to the <div> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
+ $out = '<div' . $attrib_str . ">";
+
+ $out .= rep_specialchars_output(sprintf('%s <a href="#loadimages" onclick="%s.command(\'load-images\')" title="%s">%s</a>',
+ rcube_label('blockedimages'),
+ $JS_OBJECT_NAME,
+ rcube_label('showimages'),
+ rcube_label('showimages')));
+
+ $out .= '</div>';
+
+ $OUTPUT->add_script(sprintf("%s.gui_object('remoteobjectsmsg', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+ return $out;
+ }
+
+
+if ($_action=='print')
+ parse_template('printmessage');
+else
+ parse_template('message');
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/spell.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Submit request to Google's spell checking engine |
+ | |
+ | CREDITS: |
+ | Script from GoogieSpell by amix.dk |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: spell.inc 265 2006-06-25 10:04:45Z thomasb $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+$google = "ssl://www.google.com";
+$port = 443;
+$lang = $_GET['lang'];
+$path = "/tbproxy/spell?lang=$lang";
+$data = file_get_contents('php://input');
+$store = "";
+
+if ($fp = fsockopen($google, $port, $errno, $errstr, 30))
+ {
+ $out = "POST $path HTTP/1.0\r\n";
+ $out .= "Host: $google\r\n";
+ $out .= "Content-Length: " . strlen($data) . "\r\n";
+ $out .= "Content-type: application/x-www-form-urlencoded\r\n";
+ $out .= "Connection: Close\r\n\r\n";
+ $out .= $data;
+ fwrite($fp, $out);
+
+ while (!feof($fp))
+ $store .= fgets($fp, 128);
+ fclose($fp);
+ }
+
+print $store;
+exit;
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/upload.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Handle file-upload and make them available as attachments |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: upload.inc 297 2006-08-06 15:55:11Z thomasb $
+
+*/
+
+
+if (!$_SESSION['compose'])
+ {
+ exit;
+ }
+
+
+// create temp dir for file uploads
+$temp_dir = rcmail_create_compose_tempdir();
+
+
+if (!is_array($_SESSION['compose']['attachments']))
+ $_SESSION['compose']['attachments'] = array();
+
+
+$response = '';
+
+foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath)
+ {
+ $tmpfname = tempnam($temp_dir, 'rcmAttmnt');
+ if (move_uploaded_file($filepath, $tmpfname))
+ {
+ $id = count($_SESSION['compose']['attachments']);
+ $_SESSION['compose']['attachments'][] = array('name' => $_FILES['_attachments']['name'][$i],
+ 'mimetype' => $_FILES['_attachments']['type'][$i],
+ 'path' => $tmpfname);
+
+ if (is_file($CONFIG['skin_path'] . '/images/icons/remove-attachment.png'))
+ $button = sprintf('<img src="%s/images/icons/remove-attachment.png" alt="%s" border="0" style="padding-right:2px;vertical-align:middle" />',
+ $CONFIG['skin_path'],
+ rcube_label('delete'));
+ else
+ $button = rcube_label('delete');
+
+ $content = sprintf('<a href="#delete" onclick="return %s.command(\\\'remove-attachment\\\', \\\'rcmfile%d\\\', this)" title="%s">%s</a>%s',
+ $JS_OBJECT_NAME,
+ $id,
+ rcube_label('delete'),
+ $button,
+ rep_specialchars_output($_FILES['_attachments']['name'][$i], 'js'));
+
+ $response .= sprintf('parent.%s.add2attachment_list(\'rcmfile%d\',\'%s\');',
+ $JS_OBJECT_NAME,
+ $id,
+ $content);
+ }
+ }
+
+
+// send html page with JS calls as response
+$frameout = <<<EOF
+$response
+parent.$JS_OBJECT_NAME.show_attachment_form(false);
+parent.$JS_OBJECT_NAME.auto_save_start();
+EOF;
+
+rcube_iframe_response($frameout);
+
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/viewsource.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Display a mail message similar as a usual mail application does |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: viewsource.inc 8 2005-09-28 22:28:05Z roundcube $
+
+*/
+
+
+// similar code as in program/steps/mail/get.inc
+if ($_GET['_uid'])
+ {
+ header('Content-Type: text/plain');
+ print rcmail_message_source($_GET['_uid']);
+ }
+else
+ {
+ raise_error(array('code' => 500,
+ 'type' => 'php',
+ 'message' => 'Message UID '.$_GET['_uid'].' not found'),
+ TRUE,
+ TRUE);
+ }
+
+exit;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/delete_identity.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Delete the submitted identities (IIDs) from the database |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: delete_identity.inc 429 2006-12-22 22:26:24Z thomasb $
+
+*/
+
+$REMOTE_REQUEST = $_GET['_remote'] ? TRUE : FALSE;
+
+if ($_GET['_iid'] && preg_match('/^[0-9]+(,[0-9]+)*$/',$_GET['_iid']))
+ {
+ $DB->query("UPDATE ".get_table_name('identities')."
+ SET del=1
+ WHERE user_id=?
+ AND identity_id IN (".$_GET['_iid'].")",
+ $_SESSION['user_id']);
+
+ $count = $DB->affected_rows();
+ if ($count)
+ {
+ $commands = show_message('deletedsuccessfully', 'confirmation');
+ }
+
+ // send response
+ if ($REMOTE_REQUEST)
+ rcube_remote_response($commands);
+ }
+
+
+if ($REMOTE_REQUEST)
+ exit;
+
+
+// go to identities page
+$_action = 'identities';
+
+// overwrite action variable
+$OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action));
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/edit_identity.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | 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: edit_identity.inc 88 2005-12-03 16:54:12Z roundcube $
+
+*/
+
+if (($_GET['_iid'] || $_POST['_iid']) && $_action=='edit-identity')
+ {
+ $id = $_POST['_iid'] ? $_POST['_iid'] : $_GET['_iid'];
+ $DB->query("SELECT * FROM ".get_table_name('identities')."
+ WHERE identity_id=?
+ AND user_id=?
+ AND del<>1",
+ $id,
+ $_SESSION['user_id']);
+
+ $IDENTITY_RECORD = $DB->fetch_assoc();
+
+ if (is_array($IDENTITY_RECORD))
+ $OUTPUT->add_script(sprintf("%s.set_env('iid', '%s');", $JS_OBJECT_NAME, $IDENTITY_RECORD['identity_id']));
+
+ $PAGE_TITLE = rcube_label('edititem');
+ }
+else
+ $PAGE_TITLE = rcube_label('newitem');
+
+
+
+function rcube_identity_form($attrib)
+ {
+ global $IDENTITY_RECORD, $JS_OBJECT_NAME;
+
+ if (!$IDENTITY_RECORD && $GLOBALS['_action']!='add-identity')
+ return rcube_label('notfound');
+
+ // add some labels to client
+ rcube_add_label('noemailwarning');
+ rcube_add_label('nonamewarning');
+
+
+ list($form_start, $form_end) = get_form_tags($attrib, 'save-identity', array('name' => '_iid', 'value' => $IDENTITY_RECORD['identity_id']));
+ unset($attrib['form']);
+
+
+ // list of available cols
+ $a_show_cols = array('name' => array('type' => 'text'),
+ 'email' => array('type' => 'text'),
+ 'organization' => array('type' => 'text'),
+ 'reply-to' => array('type' => 'text', 'label' => 'replyto'),
+ 'bcc' => array('type' => 'text'),
+ 'signature' => array('type' => 'textarea'),
+ 'standard' => array('type' => 'checkbox', 'label' => 'setdefault'));
+
+
+ // a specific part is requested
+ if ($attrib['part'])
+ {
+ $colprop = $a_show_cols[$attrib['part']];
+ if (is_array($colprop))
+ {
+ $out = $form_start;
+ $out .= rcmail_get_edit_field($attrib['part'], $IDENTITY_RECORD[$attrib['part']], $attrib, $colprop['type']);
+ return $out;
+ }
+ else
+ return '';
+ }
+
+
+ // return the complete edit form as table
+ $out = "$form_start<table>\n\n";
+
+ foreach ($a_show_cols as $col => $colprop)
+ {
+ $attrib['id'] = 'rcmfd_'.$col;
+ $label = strlen($colprop['label']) ? $colprop['label'] : $col;
+ $value = rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $attrib, $colprop['type']);
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $attrib['id'],
+ rep_specialchars_output(rcube_label($label)),
+ $value);
+ }
+
+ $out .= "\n</table>$form_end";
+
+ return $out;
+ }
+
+
+
+if ($_action=='add-identity' && template_exists('addidentity'))
+ parse_template('addidentity');
+
+parse_template('editidentity');
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/func.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide functionality for user's settings & preferences |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: func.inc 171 2006-03-23 22:32:47Z roundcube $
+
+*/
+
+
+// get user record
+$sql_result = $DB->query("SELECT username, mail_host FROM ".get_table_name('users')."
+ WHERE user_id=?",
+ $_SESSION['user_id']);
+
+if ($USER_DATA = $DB->fetch_assoc($sql_result))
+ $PAGE_TITLE = sprintf('%s %s@%s', rcube_label('settingsfor'), $USER_DATA['username'], $USER_DATA['mail_host']);
+
+
+
+function rcmail_user_prefs_form($attrib)
+ {
+ global $DB, $CONFIG, $sess_user_lang;
+
+ // add some labels to client
+ rcube_add_label('nopagesizewarning');
+
+ list($form_start, $form_end) = get_form_tags($attrib, 'save-prefs');
+ unset($attrib['form']);
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+
+ // return the complete edit form as table
+ $out = "$form_start<table" . $attrib_str . ">\n\n";
+
+ $a_show_cols = array('language' => array('type' => 'text'),
+ 'pagesize' => array('type' => 'text'),
+ 'timezone' => array('type' => 'text'),
+ 'prettydate' => array('type' => 'text'));
+
+ // show language selection
+ $a_lang = rcube_list_languages();
+ asort($a_lang);
+
+ $field_id = 'rcmfd_lang';
+ $select_lang = new select(array('name' => '_language', 'id' => $field_id));
+ $select_lang->add(array_values($a_lang), array_keys($a_lang));
+
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output(rcube_label('language')),
+ $select_lang->show($sess_user_lang));
+
+
+ // show page size selection
+ $field_id = 'rcmfd_timezone';
+ $select_timezone = new select(array('name' => '_timezone', 'id' => $field_id));
+ $select_timezone->add('(GMT -11:00) Midway Island, Samoa', '-11');
+ $select_timezone->add('(GMT -10:00) Hawaii', '-10');
+ $select_timezone->add('(GMT -9:00) Alaska', '-9');
+ $select_timezone->add('(GMT -8:00) Pacific Time (US/Canada)', '-8');
+ $select_timezone->add('(GMT -7:00) Mountain Time (US/Canada)', '-7');
+ $select_timezone->add('(GMT -6:00) Central Time (US/Canada), Mexico City', '-6');
+ $select_timezone->add('(GMT -5:00) Eastern Time (US/Canada), Bogota, Lima', '-5');
+ $select_timezone->add('(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz', '-4');
+ $select_timezone->add('(GMT -3:00) Brazil, Buenos Aires, Georgetown', '-3');
+ $select_timezone->add('(GMT -3:30) Nfld Time (Canada), Nfld, S. Labador', '-3.5');
+ $select_timezone->add('(GMT -2:00) Mid-Atlantic', '-2');
+ $select_timezone->add('(GMT -1:00) Azores, Cape Verde Islands', '-1');
+ $select_timezone->add('(GMT) Western Europe, London, Lisbon, Casablanca', '0');
+ $select_timezone->add('(GMT +1:00) Central European Time', '1');
+ $select_timezone->add('(GMT +2:00) EET: Kaliningrad, South Africa', '2');
+ $select_timezone->add('(GMT +3:00) Baghdad, Kuwait, Riyadh, Moscow, Nairobi', '3');
+ $select_timezone->add('(GMT +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 +7:00) Bangkok, Hanoi, Jakarta', '7');
+ $select_timezone->add('(GMT +8:00) Beijing, Perth, Singapore, Taipei', '8');
+ $select_timezone->add('(GMT +9:00) Tokyo, Seoul, Yakutsk', '9');
+ $select_timezone->add('(GMT +9:30) Adelaide, Darwin', '9.5');
+ $select_timezone->add('(GMT +10:00) EAST/AEST: Guam, Vladivostok', '10');
+ $select_timezone->add('(GMT +11:00) Magadan, Solomon Islands', '11');
+ $select_timezone->add('(GMT +12:00) Auckland, Wellington, Kamchatka', '12');
+ $select_timezone->add('(GMT +13:00) Tonga, Pheonix Islands', '13');
+ $select_timezone->add('(GMT +14:00) Kiribati', '14');
+
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output(rcube_label('timezone')),
+ $select_timezone->show($CONFIG['timezone']));
+
+
+ $field_id = 'rcmfd_dst';
+ $input_dst = new checkbox(array('name' => '_dst_active', 'id' => $field_id, 'value' => 1));
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output(rcube_label('dstactive')),
+ $input_dst->show($CONFIG['dst_active']));
+
+
+ // show page size selection
+ $field_id = 'rcmfd_pgsize';
+ $input_pagesize = new textfield(array('name' => '_pagesize', 'id' => $field_id, 'size' => 5));
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output(rcube_label('pagesize')),
+ $input_pagesize->show($CONFIG['pagesize']));
+
+ // show checkbox for HTML/plaintext messages
+ $field_id = 'rcmfd_htmlmsg';
+ $input_pagesize = new checkbox(array('name' => '_prefer_html', 'id' => $field_id, 'value' => 1));
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output(rcube_label('preferhtml')),
+ $input_pagesize->show($CONFIG['prefer_html']?1:0));
+
+ // MM: Show checkbox for toggling 'pretty dates'
+ $field_id = 'rcmfd_prettydate';
+ $input_prettydate = new checkbox(array('name' => '_pretty_date', 'id' => $field_id, 'value' => 1));
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output(rcube_label('prettydate')),
+ $input_prettydate->show($CONFIG['prettydate']?1:0));
+
+
+ $out .= "\n</table>$form_end";
+
+ return $out;
+ }
+
+
+
+
+function rcmail_identities_list($attrib)
+ {
+ global $DB, $CONFIG, $OUTPUT, $JS_OBJECT_NAME;
+
+
+ // get contacts from DB
+ $sql_result = $DB->query("SELECT * FROM ".get_table_name('identities')."
+ WHERE del<>1
+ AND user_id=?
+ ORDER BY standard DESC, name ASC",
+ $_SESSION['user_id']);
+
+
+ // add id to message list table if not specified
+ if (!strlen($attrib['id']))
+ $attrib['id'] = 'rcmIdentitiesList';
+
+ // define list of cols to be displayed
+ $a_show_cols = array('name', 'email', 'organization', 'reply-to');
+
+ // create XHTML table
+ $out = rcube_table_output($attrib, $sql_result, $a_show_cols, 'identity_id');
+
+ // set client env
+ $javascript = sprintf("%s.gui_object('identitieslist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
+ $OUTPUT->add_script($javascript);
+
+ return $out;
+ }
+
+
+
+// similar function as in /steps/addressbook/edit.inc
+function get_form_tags($attrib, $action, $add_hidden=array())
+ {
+ global $OUTPUT, $JS_OBJECT_NAME, $EDIT_FORM, $SESS_HIDDEN_FIELD;
+
+ $form_start = '';
+ if (!strlen($EDIT_FORM))
+ {
+ $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task']));
+ $hiddenfields->add(array('name' => '_action', 'value' => $action));
+
+ if ($add_hidden)
+ $hiddenfields->add($add_hidden);
+
+ if ($_GET['_framed'] || $_POST['_framed'])
+ $hiddenfields->add(array('name' => '_framed', 'value' => 1));
+
+ $form_start = !strlen($attrib['form']) ? '<form name="form" action="./" method="post">' : '';
+ $form_start .= "\n$SESS_HIDDEN_FIELD\n";
+ $form_start .= $hiddenfields->show();
+ }
+
+ $form_end = (!strlen($EDIT_FORM) && !strlen($attrib['form'])) ? '</form>' : '';
+ $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form';
+
+ if (!strlen($EDIT_FORM))
+ $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('editform', '$form_name');");
+
+ $EDIT_FORM = $form_name;
+
+ return array($form_start, $form_end);
+ }
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/identities.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Manage identities of a user account |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: identities.inc 8 2005-09-28 22:28:05Z roundcube $
+
+*/
+
+if ($USER_DATA = $DB->fetch_assoc($sql_result))
+ $PAGE_TITLE = sprintf('%s (%s@%s)', rcube_label('identities'), $USER_DATA['username'], $USER_DATA['mail_host']);
+
+
+
+// similar function as /steps/addressbook/func.inc::rcmail_contact_frame()
+function rcmail_identity_frame($attrib)
+ {
+ global $OUTPUT, $JS_OBJECT_NAME;
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmIdentityFrame';
+
+ $attrib['name'] = $attrib['id'];
+
+ $OUTPUT->add_script(sprintf("%s.set_env('contentframe', '%s');", $JS_OBJECT_NAME, $attrib['name']));
+
+ $attrib_str = create_attrib_string($attrib, array('name', 'id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
+ $out = '<iframe'. $attrib_str . '></iframe>';
+
+ return $out;
+ }
+
+
+
+parse_template('identities');
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/manage_folders.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Provide functionality to create/delete/rename folders |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: manage_folders.inc 285 2006-07-30 19:38:06Z thomasb $
+
+*/
+
+// init IMAP connection
+rcmail_imap_init(TRUE);
+
+
+// subscribe to one or more mailboxes
+if ($_action=='subscribe')
+ {
+ if (strlen($_GET['_mboxes']))
+ $IMAP->subscribe(array($_GET['_mboxes']));
+
+ if ($REMOTE_REQUEST)
+ rcube_remote_response('// subscribed');
+ }
+
+// unsubscribe one or more mailboxes
+else if ($_action=='unsubscribe')
+ {
+ if (strlen($_GET['_mboxes']))
+ $IMAP->unsubscribe(array($_GET['_mboxes']));
+
+ if ($REMOTE_REQUEST)
+ rcube_remote_response('// unsubscribed');
+ }
+
+// create a new mailbox
+else if ($_action=='create-folder')
+ {
+ if (!empty($_GET['_name']))
+ $create = $IMAP->create_mailbox(trim(get_input_value('_name', RCUBE_INPUT_GET)), TRUE);
+
+ if ($create && $REMOTE_REQUEST)
+ {
+ $commands = sprintf("this.add_folder_row('%s')", rep_specialchars_output($create, 'js'));
+ rcube_remote_response($commands);
+ }
+ else if (!$create && $REMOTE_REQUEST)
+ {
+ $commands = show_message('errorsaving', 'error');
+ rcube_remote_response($commands);
+ }
+ else if (!$create)
+ show_message('errorsaving', 'error');
+ }
+
+// rename a mailbox
+else if ($_action=='rename-folder')
+ {
+ if (!empty($_GET['_folder_oldname']) && !empty($_GET['_folder_newname']))
+ $rename = $IMAP->rename_mailbox(get_input_value('_folder_oldname', RCUBE_INPUT_GET), trim(get_input_value('_folder_newname', RCUBE_INPUT_GET)));
+
+ if ($rename && $REMOTE_REQUEST)
+ {
+ $commands = sprintf("this.replace_folder_row('%s','%s');",
+ addslashes(rep_specialchars_output($rename, 'js')),
+ rep_specialchars_output($_GET['_folder_oldname'], 'js'));
+ rcube_remote_response($commands);
+ }
+ else if (!$rename && $REMOTE_REQUEST)
+ {
+ $commands = "this.reset_folder_rename();\n";
+ $commands .= show_message('errorsaving', 'error');
+ rcube_remote_response($commands);
+ }
+ else if (!$rename)
+ show_message('errorsaving', 'error');
+ }
+
+// delete an existing IMAP mailbox
+else if ($_action=='delete-folder')
+ {
+ if (!empty($_GET['_mboxes']))
+ $deleted = $IMAP->delete_mailbox(array(get_input_value('_mboxes', RCUBE_INPUT_GET)));
+
+ if ($REMOTE_REQUEST && $deleted)
+ {
+ $commands = sprintf("this.remove_folder_row('%s');\n", rep_specialchars_output(get_input_value('_mboxes', RCUBE_INPUT_GET), 'js'));
+ $commands .= show_message('folderdeleted', 'confirmation');
+ rcube_remote_response($commands);
+ }
+ else if ($REMOTE_REQUEST)
+ {
+ $commands = show_message('errorsaving', 'error');
+ rcube_remote_response($commands);
+ }
+ }
+
+
+
+// build table with all folders listed by server
+function rcube_subscription_form($attrib)
+ {
+ global $IMAP, $CONFIG, $OUTPUT, $JS_OBJECT_NAME;
+
+ list($form_start, $form_end) = get_form_tags($attrib, 'folders');
+ unset($attrib['form']);
+
+
+ if (!$attrib['id'])
+ $attrib['id'] = 'rcmSubscriptionlist';
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+
+ $out = "$form_start\n<table" . $attrib_str . ">\n";
+
+
+ // add table header
+ $out .= "<thead><tr>\n";
+ $out .= sprintf('<td class="name">%s</td><td class="subscribed">%s</td>'.
+ '<td class="rename"> </td><td class="delete"> </td>',
+ rcube_label('foldername'), rcube_label('subscribed'));
+
+ $out .= "\n</tr></thead>\n<tbody>\n";
+
+
+ // get folders from server
+ $a_unsubscribed = $IMAP->list_unsubscribed();
+ $a_subscribed = $IMAP->list_mailboxes();
+ $a_js_folders = array();
+
+ $checkbox_subscribe = new checkbox(array('name' => '_subscribed[]', 'onclick' => "$JS_OBJECT_NAME.command(this.checked?'subscribe':'unsubscribe',this.value)"));
+
+ if (!empty($attrib['deleteicon']))
+ $del_button = sprintf('<img src="%s%s" alt="%s" border="0" />', $CONFIG['skin_path'], $attrib['deleteicon'], rcube_label('delete'));
+ else
+ $del_button = rcube_label('delete');
+
+ if (!empty($attrib['renameicon']))
+ $edit_button = sprintf('<img src="%s%s" alt="%s" border="0" />', $CONFIG['skin_path'], $attrib['renameicon'], rcube_label('rename'));
+ else
+ $del_button = rcube_label('rename');
+
+ // create list of available folders
+ foreach ($a_unsubscribed as $i => $folder)
+ {
+ $protected = ($CONFIG['protect_default_folders'] == TRUE && in_array($folder,$CONFIG['default_imap_folders']));
+ $zebra_class = $i%2 ? 'even' : 'odd';
+ $folder_js = rep_specialchars_output($folder, 'js');
+
+ if (!$protected)
+ $a_js_folders['rcmrow'.($i+1)] = $folder_js;
+
+ $out .= sprintf('<tr id="rcmrow%d" class="%s"><td>%s</td><td>%s</td>',
+ $i+1,
+ $zebra_class,
+ rep_specialchars_output(rcube_charset_convert($folder, 'UTF-7', 'UTF-8'), 'html', 'all'),
+ $checkbox_subscribe->show(in_array($folder, $a_subscribed)?$folder:'', array('value' => $folder, 'disabled' => $protected)));
+
+ // add rename and delete buttons
+ if (!$protected)
+ $out .= sprintf('<td><a href="#rename" onclick="%s.command(\'rename-folder\',\'%s\')" title="%s">%s</a>'.
+ '<td><a href="#delete" onclick="%s.command(\'delete-folder\',\'%s\')" title="%s">%s</a></td>',
+ $JS_OBJECT_NAME,
+ $folder_js,
+ rcube_label('renamefolder'),
+ $edit_button,
+ $JS_OBJECT_NAME,
+ $folder_js,
+ rcube_label('deletefolder'),
+ $del_button);
+ else
+ $out .= '<td></td><td></td>';
+
+ $out .= "</tr>\n";
+ }
+
+ $out .= "</tbody>\n</table>";
+ $out .= "\n$form_end";
+
+
+ $javascript = sprintf("%s.gui_object('subscriptionlist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
+ $javascript .= sprintf("%s.set_env('subscriptionrows', %s);", $JS_OBJECT_NAME, array2js($a_js_folders));
+ $OUTPUT->add_script($javascript);
+
+ return $out;
+ }
+
+
+function rcube_create_folder_form($attrib)
+ {
+ global $JS_OBJECT_NAME;
+
+ list($form_start, $form_end) = get_form_tags($attrib, 'create-folder');
+ unset($attrib['form']);
+
+
+ // return the complete edit form as table
+ $out = "$form_start\n";
+
+ $input = new textfield(array('name' => '_folder_name'));
+ $out .= $input->show();
+
+ if (get_boolean($attrib['button']))
+ {
+ $button = new input_field(array('type' => 'button',
+ 'value' => rcube_label('create'),
+ 'onclick' => "$JS_OBJECT_NAME.command('create-folder',this.form)"));
+ $out .= $button->show();
+ }
+
+ $out .= "\n$form_end";
+
+ return $out;
+ }
+
+function rcube_rename_folder_form($attrib)
+ {
+ global $CONFIG, $IMAP, $JS_OBJECT_NAME;
+
+ list($form_start, $form_end) = get_form_tags($attrib, 'rename-folder');
+ unset($attrib['form']);
+
+ // return the complete edit form as table
+ $out = "$form_start\n";
+
+ $a_unsubscribed = $IMAP->list_unsubscribed();
+ $select_folder = new select(array('name' => '_folder_oldname', 'id' => 'rcmfd_oldfolder'));
+
+ foreach ($a_unsubscribed as $i => $folder)
+ {
+ if ($CONFIG['protect_default_folders'] == TRUE && in_array($folder,$CONFIG['default_imap_folders']))
+ continue;
+
+ $select_folder->add($folder);
+ }
+
+ $out .= $select_folder->show();
+
+ $out .= " to ";
+ $inputtwo = new textfield(array('name' => '_folder_newname'));
+ $out .= $inputtwo->show();
+
+ if (get_boolean($attrib['button']))
+ {
+ $button = new input_field(array('type' => 'button',
+ 'value' => rcube_label('rename'),
+ 'onclick' => "$JS_OBJECT_NAME.command('rename-folder',this.form)"));
+ $out .= $button->show();
+ }
+
+ $out .= "\n$form_end";
+
+ return $out;
+ }
+
+
+// add some labels to client
+rcube_add_label('deletefolderconfirm');
+
+
+parse_template('managefolders');
+?>
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/save_identity.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Save an identity record or to add a new one |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: save_identity.inc 430 2006-12-22 22:31:38Z thomasb $
+
+*/
+
+$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'standard', 'signature');
+$a_html_cols = array('signature');
+
+
+// check input
+if (empty($_POST['_name']) || empty($_POST['_email']))
+ {
+ show_message('formincomplete', 'warning');
+ rcmail_overwrite_action('edit-identitiy');
+ return;
+ }
+
+
+// update an existing contact
+if ($_POST['_iid'])
+ {
+ $a_write_sql = array();
+
+ foreach ($a_save_cols as $col)
+ {
+ $fname = '_'.$col;
+ if (!isset($_POST[$fname]))
+ continue;
+
+ $a_write_sql[] = sprintf("%s=%s",
+ $DB->quoteIdentifier($col),
+ $DB->quote(get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols))));
+ }
+
+ if (sizeof($a_write_sql))
+ {
+ $DB->query("UPDATE ".get_table_name('identities')."
+ SET ".join(', ', $a_write_sql)."
+ WHERE identity_id=?
+ AND user_id=?
+ AND del<>1",
+ $_POST['_iid'],
+ $_SESSION['user_id']);
+
+ $updated = $DB->affected_rows();
+ }
+
+ if ($updated && !empty($_POST['_standard']))
+ {
+ show_message('successfullysaved', 'confirmation');
+
+ // mark all other identities as 'not-default'
+ $DB->query("UPDATE ".get_table_name('identities')."
+ SET ".$DB->quoteIdentifier('standard')."='0'
+ WHERE user_id=?
+ AND identity_id<>?
+ AND del<>1",
+ $_SESSION['user_id'],
+ $_POST['_iid']);
+
+ if ($_POST['_framed'])
+ {
+ // update the changed col in list
+ // ...
+ }
+ }
+ else if ($DB->is_error())
+ {
+ // show error message
+ show_message('errorsaving', 'error');
+ rcmail_overwrite_action('edit-identitiy');
+ }
+ }
+
+// insert a new contact
+else
+ {
+ $a_insert_cols = $a_insert_values = array();
+
+ foreach ($a_save_cols as $col)
+ {
+ $fname = '_'.$col;
+ if (!isset($_POST[$fname]))
+ continue;
+
+ $a_insert_cols[] = $DB->quoteIdentifier($col);
+ $a_insert_values[] = $DB->quote(get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols)));
+ }
+
+ if (sizeof($a_insert_cols))
+ {
+ $DB->query("INSERT INTO ".get_table_name('identities')."
+ (user_id, ".join(', ', $a_insert_cols).")
+ VALUES (?, ".join(', ', $a_insert_values).")",
+ $_SESSION['user_id']);
+
+ $insert_id = $DB->insert_id(get_sequence_name('identities'));
+ }
+
+ if ($insert_id)
+ {
+ $_GET['_iid'] = $insert_id;
+
+ if ($_POST['_framed'])
+ {
+ // add contact row or jump to the page where it should appear
+ // ....
+ }
+ }
+ else
+ {
+ // show error message
+ show_message('errorsaving', 'error');
+ rcmail_overwrite_action('edit-identity');
+ }
+ }
+
+
+// go to next step
+rcmail_overwrite_action($_POST['_framed'] ? 'edit-identity' : 'identities');
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/settings/save_prefs.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Save user preferences to DB and to the current session |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: save_prefs.inc 194 2006-04-13 18:23:48Z roundcube $
+
+*/
+
+$a_user_prefs = $_SESSION['user_prefs'];
+if (!is_array($a_user_prefs))
+ $a_user_prefs = array();
+
+
+$a_user_prefs['timezone'] = isset($_POST['_timezone']) ? floatval($_POST['_timezone']) : $CONFIG['timezone'];
+$a_user_prefs['dst_active'] = isset($_POST['_dst_active']) ? TRUE : FALSE;
+$a_user_prefs['pagesize'] = is_numeric($_POST['_pagesize']) ? (int)$_POST['_pagesize'] : $CONFIG['pagesize'];
+$a_user_prefs['prefer_html'] = isset($_POST['_prefer_html']) ? TRUE : FALSE;
+
+// MM: Date format toggle (Pretty / Standard)
+$a_user_prefs['prettydate'] = isset($_POST['_pretty_date']) ? TRUE : FALSE;
+
+if (isset($_POST['_language']))
+ {
+ $sess_user_lang = $_SESSION['user_lang'] = $_POST['_language'];
+ rcmail_set_locale($sess_user_lang);
+ }
+
+if (rcmail_save_user_prefs($a_user_prefs))
+ show_message('successfullysaved', 'confirmation');
+
+
+// go to next step
+$_action = 'preferences';
+
+// overwrite action variable
+$OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action));
+
+?>
\ No newline at end of file
--- /dev/null
+/***** RoundCube|Mail address book task styles *****/
+
+
+#abooktoolbar
+{
+ position: absolute;
+ top: 45px;
+ left: 200px;
+ height: 35px;
+}
+
+#abooktoolbar a
+{
+ padding-right: 10px;
+}
+
+#abookcountbar
+{
+ position: absolute;
+ top: 60px;
+ left: 490px;
+ width: 240px;
+ height: 20px;
+ text-align: left;
+}
+
+#abookcountbar span
+{
+ font-size: 11px;
+ color: #333333;
+}
+
+
+#addresslist
+{
+ position: absolute;
+ top: 85px;
+ left: 20px;
+ width: 450px;
+ bottom: 40px;
+ border: 1px solid #999999;
+ background-color: #F9F9F9;
+ overflow: auto;
+ /* css hack for IE */
+ height: expression((parseInt(document.documentElement.clientHeight)-135)+'px');
+}
+
+#contacts-table
+{
+ width: 100%;
+ table-layout: fixed;
+ /* css hack for IE */
+ width: expression(document.getElementById('addresslist').clientWidth);
+}
+
+
+#contacts-table tbody td
+{
+ cursor: pointer;
+}
+
+
+#contacts-box
+{
+ position: absolute;
+ top: 85px;
+ left: 490px;
+ right: 40px;
+ bottom: 40px;
+ border: 1px solid #999999;
+ overflow: hidden;
+ /* css hack for IE */
+ width: expression((parseInt(document.documentElement.clientWidth)-530)+'px');
+ height: expression((parseInt(document.documentElement.clientHeight)-135)+'px');
+}
+
+body.iframe,
+#contact-frame
+{
+ background-color: #F9F9F9;
+}
+
+#contact-frame
+{
+ border: none;
+/* visibility: hidden; */
+}
+
+#contact-title
+{
+ height: 12px !important;
+/* height: 20px; */
+ padding: 4px 20px 3px 20px;
+ border-bottom: 1px solid #999999;
+ color: #333333;
+ font-size: 11px;
+ font-weight: bold;
+ background-color: #EBEBEB;
+ background-image: url(images/listheader_aqua.gif);
+}
+
+#contact-details
+{
+ padding: 15px 20px 10px 20px;
+}
+
+#contact-details table td.title
+{
+ color: #666666;
+ font-weight: bold;
+ text-align: right;
+ padding-right: 10px;
+}
+
+
+
+
+
+
--- /dev/null
+/***** RoundCube|Mail basic styles *****/
+
+body
+{
+ margin: 8px;
+ background-color: #F6F6F6; /* #EBEBEB; */
+ color: #000000;
+}
+
+body.iframe
+{
+ margin: 0px;
+}
+
+body.extwin
+{
+ margin: 10px;
+}
+
+body, td, th, span, div, p, h3
+{
+ font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+ font-size: 12px;
+ color: #000000;
+}
+
+th
+{
+ font-weight: normal;
+}
+
+h3
+{
+ font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+ font-size: 18px;
+ color: #000000;
+}
+
+a, a:active, a:visited
+{
+ color: #000000;
+ outline: none;
+}
+
+a.button, a.button:visited, a.tab, a.tab:visited, a.axislist
+{
+ color: #000000;
+ text-decoration: none;
+}
+
+a.tab
+{
+ width: 80px;
+ display: block;
+ text-align: center;
+}
+
+hr
+{
+ height: 1px;
+ background-color: #666666;
+ border-style: none;
+}
+
+input, textarea
+{
+ font-size: 9pt;
+ font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+ padding: 1px;
+ padding-left: 3px;
+ padding-right: 3px;
+ background-color: #ffffff;
+ border: 1px solid #666666;
+}
+
+input.button
+{
+ height: 20px;
+ color: #333333;
+ font-size: 12px;
+ padding-left: 8px;
+ padding-right: 8px;
+ background: url(images/buttons/bg.gif) repeat-x #f0f0f0;
+ border: 1px solid #a4a4a4;
+}
+
+input.button:hover
+{
+ color: black;
+}
+
+img
+{
+ behavior: url('skins/default/pngbehavior.htc');
+}
+
+.alttext
+{
+ font-size: 11px;
+}
+
+
+/** common user interface objects */
+
+#header
+{
+ position: absolute;
+ top: 10px;
+ left: 20px;
+ width: 170px;
+ height: 40px;
+ z-index: 100;
+}
+
+#taskbar
+{
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ width: 600px;
+ height: 37px;
+ background: url(images/taskbar.gif) top right no-repeat;
+ padding: 10px 24px 0px 0px;
+ text-align: right;
+ white-space: nowrap;
+ z-index: 2;
+}
+
+#taskbar a,
+#taskbar a:active,
+#taskbar a:visited
+{
+ font-size: 11px;
+ color: #666666;
+ text-decoration: none;
+ padding: 6px 16px 6px 30px;
+ background-repeat: no-repeat;
+}
+
+#taskbar a:hover
+{
+ color: #333333;
+}
+
+a.button-mail
+{
+ background-image: url(images/buttons/mail.gif);
+}
+
+a.button-addressbook
+{
+ background-image: url(images/buttons/addressbook.gif);
+}
+
+a.button-settings
+{
+ background-image: url(images/buttons/settings.gif);
+}
+
+a.button-logout
+{
+ background-image: url(images/buttons/logout.gif);
+}
+
+
+#message
+{
+ position: absolute;
+ display: none;
+ top: -1px;
+ left: 200px;
+ right: 200px;
+ z-index: 5000;
+ opacity: 0.85;
+}
+
+#message div
+{
+ width: 400px;
+ margin: 0px auto;
+ height: 24px;
+ min-height: 24px;
+ padding: 8px 10px 8px 46px;
+}
+
+#message div.notice,
+#remote-objects-message
+{
+ background: url(images/display/info.png) 6px 3px no-repeat;
+ background-color: #F7FDCB;
+ border: 1px solid #C2D071;
+}
+
+#message div.error,
+#message div.warning
+{
+ background: url(images/display/warning.png) 6px 3px no-repeat;
+ background-color: #EF9398;
+ border: 1px solid #DC5757;
+}
+
+#message div.confirmation
+{
+ background: url(images/display/confirm.png) 6px 3px no-repeat;
+ background-color: #A6EF7B;
+ border: 1px solid #76C83F;
+}
+
+#message div.loading
+{
+ background: url(images/display/loading.gif) 6px 3px no-repeat;
+ background-color: #EBEBEB;
+ border: 1px solid #CCCCCC;
+}
+
+
+/***** common table settings ******/
+
+table.records-table thead tr td
+{
+ height: 20px;
+ padding: 0px 4px 0px 4px;
+ vertical-align: middle;
+ border-bottom: 1px solid #999999;
+ color: #333333;
+ background-color: #EBEBEB;
+ background-image: url(images/listheader_aqua.gif);
+ font-size: 11px;
+ font-weight: bold;
+}
+
+table.records-table tbody tr td
+{
+ height: 16px;
+ padding: 2px 4px 2px 4px;
+ font-size: 11px;
+ white-space: nowrap;
+ border-bottom: 1px solid #EBEBEB;
+ overflow: hidden;
+ text-align: left;
+}
+
+table.records-table tr
+{
+ background-color: #FFFFFF;
+}
+
+table.records-table tr.selected td
+{
+ font-weight: bold;
+ color: #FFFFFF;
+ background-color: #CC3333;
+}
+
+
+
+/***** roundcube webmail pre-defined classes *****/
+
+a.rcmContactAddress
+{
+ text-decoration: none;
+}
+
+a.rcmContactAddress:hover
+{
+ text-decoration: underline;
+}
+
+#rcmKSearchpane
+{
+ background-color: #F9F9F9;
+ border: 1px solid #CCCCCC;
+}
+
+#rcmKSearchpane ul
+{
+ margin: 0px;
+ padding: 2px;
+ list-style-image: none;
+ list-style-type: none;
+}
+
+#rcmKSearchpane ul li
+{
+ height: 16px;
+ font-size: 11px;
+ padding-left: 8px;
+ padding-top: 2px;
+ padding-right: 8px;
+ white-space: nowrap;
+}
+
+#rcmKSearchpane ul li.selected
+{
+ color: #ffffff;
+ background-color: #CC3333;
+}
+
--- /dev/null
+/***** modified styles for GoogieSpell *****/
+
+.googie_window {
+ font-size: 11px;
+ width: 185px;
+ text-align: left;
+ border: 1px solid #666666;
+ background-color: #ffffff;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ visibility: hidden;
+}
+
+.googie_list {
+ width: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+.googie_list td {
+ font-size: 11px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ cursor: pointer;
+ list-style-type: none;
+}
+
+.googie_list_onhover {
+ color: #FFFFFF;
+ background-color: #CC3333;
+}
+
+.googie_list_onout {
+ background-color: #F6F6F6;
+}
+
+.googie_list_selected {
+ background-color: #cccccc;
+ font-weight: bold;
+}
+
+.googie_list_close {
+ font-size: 11px;
+ color: #b91414;
+}
+
+.googie_list_onhover .googie_list_close {
+ color: #FFFFFF;
+}
+
+.googie_list_revert {
+ font-size: 11px;
+ color: #b91479;
+}
+
+.googie_link {
+ color: #b91414;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+.googie_check_spelling_link {
+ color: #CC0000;
+ font-size: 11px;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.googie_check_spelling_link:hover {
+ text-decoration: underline;
+}
+
+.googie_no_style {
+ text-decoration: none;
+}
+
+.googie_check_spelling_ok {
+ color: green;
+ font-size: 11px;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+.googie_lang_3d_click img {
+ vertical-align: middle;
+ border-top: 1px solid #555;
+ border-left: 1px solid #555;
+ border-right: 1px solid #b1b1b1;
+ border-bottom: 1px solid #b1b1b1;
+}
+
+.googie_lang_3d_on img {
+ vertical-align: middle;
+ border-top: 1px solid #b1b1b1;
+ border-left: 1px solid #b1b1b1;
+ border-right: 1px solid #555;
+ border-bottom: 1px solid #555;
+}
--- /dev/null
+<div id="header"><roundcube:button command="mail" image="/images/roundcube_logo.png" alt="RoundCube Webmail" width="165" height="55" /></div>
+
+<roundcube:object name="message" id="message" />
--- /dev/null
+<script type="text/javascript">
+var ldap_server_select = document.getElementById('rcfmd_ldap_public_servers');
+
+if (ldap_server_select) {
+ // attach event to ldap server drop down
+ ldap_server_select.onchange = function() {
+ updateLdapSearchFields(this);
+ return false;
+ }
+
+ // update the fields on page load
+ updateLdapSearchFields(ldap_server_select);
+}
+
+/**
+ * function to change the attributes of the ldap server search fields select box
+ * this function is triggered by an onchange event in the server select box
+ */
+function updateLdapSearchFields(element) {
+
+ // get the search fields select element
+ var search_fields = document.getElementById('rcfmd_ldap_public_search_field');
+
+ // get rid of the current options for the select
+ for (i = search_fields.length - 1; i>=0; i--)
+ search_fields.remove(i);
+
+ // get the array containing this servers search fields
+ var server_fields = rcmail.env[element.value + '_search_fields'];
+
+ // add a new option for each of the possible search fields for the selected server
+ for (i = 0; i < server_fields.length; i++) {
+
+ // the last array value is for fuzzy search, so skip that one
+ if (i < (server_fields.length - 1)) {
+ var new_option = document.createElement('option');
+ new_option.text = server_fields[i][0];
+ new_option.value = server_fields[i][1];
+
+ // standards compliant browsers
+ try {
+ search_fields.add(new_option, null);
+ }
+ // for the standards challenged one...
+ catch(e) {
+ search_fields.add(new_option);
+ }
+ } else {
+ // ok, last member of array, so check the value of fuzzy_search
+ var fuzzy_search = server_fields[i];
+ var search_check_box = document.getElementById('rcmfd_ldap_public_search_type');
+
+ if (fuzzy_search == 'fuzzy') {
+ // we should enable the check box
+ if (search_check_box.disabled)
+ search_check_box.disabled = false;
+
+ // make sure the checkbox is unchecked
+ if (search_check_box.checked)
+ search_check_box.checked = false;
+
+ } else {
+ // we should disable the check box
+ if (!search_check_box.disabled)
+ search_check_box.disabled = true;
+
+ // check the checkbox (just a visual clue for the user)
+ if (!search_check_box.checked)
+ search_check_box.checked = true;
+ }
+ }
+ }
+}
+</script>
--- /dev/null
+<link rel="index" href="$__comm_path" />
+<link rel="shortcut icon" href="/images/favicon.ico"/>
+<link rel="stylesheet" type="text/css" href="/common.css" />
\ No newline at end of file
--- /dev/null
+<script type="text/javascript">
+
+if (window.rcmail && rcmail.env.action)
+ {
+ var action = rcmail.env.action=='preferences' ? 'default' : (rcmail.env.action.indexOf('identity')>0 ? 'identities' : rcmail.env.action);
+ var tab = document.getElementById('settingstab'+action);
+ }
+else
+ var tab = document.getElementById('settingstabdefault');
+
+if (tab)
+ tab.className = 'tablink-selected';
+
+</script>
\ No newline at end of file
--- /dev/null
+<div id="tabsbar">
+<span id="settingstabdefault" class="tablink"><roundcube:button command="preferences" type="link" label="preferences" title="editpreferences" /></span><span id="settingstabfolders" class="tablink"><roundcube:button command="folders" type="link" label="folders" title="managefolders" class="tablink" /></span><span id="settingstabidentities" class="tablink"><roundcube:button command="identities" type="link" label="identities" title="manageidentities" class="tablink" /></span>
+</div>
--- /dev/null
+<div id="taskbar">
+<roundcube:button command="mail" label="mail" class="button-mail" />
+<roundcube:button command="addressbook" label="addressbook" class="button-addressbook" />
+<roundcube:button command="settings" label="settings" class="button-settings" />
+<roundcube:button command="logout" label="logout" class="button-logout" />
+</div>
\ No newline at end of file
--- /dev/null
+/***** RoundCube|Mail address book task styles *****/
+
+
+body.iframe,
+{
+ background-color: #F9F9F9;
+}
+
+#ldapsearch-title
+{
+ height: 12px !important;
+/* height: 20px; */
+ padding: 4px 20px 3px 20px;
+ border-bottom: 1px solid #999999;
+ color: #333333;
+ font-size: 11px;
+ font-weight: bold;
+ background-color: #EBEBEB;
+ background-image: url(images/listheader_aqua.gif);
+}
+
+#ldapsearch-details
+{
+ padding: 15px 20px 10px 20px;
+}
+
+#ldapsearch-details table td.title
+{
+ color: #666666;
+ font-weight: bold;
+ text-align: right;
+ padding-right: 10px;
+}
+
+#ldapAddressList
+{
+ width: 100%;
+ table-layout: fixed;
+ /* css hack for IE */
+ width: expression(document.getElementById('addresslist').clientWidth);
+}
+
+#ldapAddressList table
+{
+ border-top: 1px solid #999999;
+}
+
+#ldap-search-results div
+{
+ width: 100%;
+ color: red;
+ background-color: green;
+}
+
--- /dev/null
+/***** RoundCube|Mail mail task styles *****/
+
+
+#messagetoolbar
+{
+ position: absolute;
+ top: 45px;
+ left: 200px;
+ right: 250px;
+ height: 35px;
+ white-space: nowrap;
+/* border: 1px solid #cccccc; */
+}
+
+#messagetoolbar a
+{
+ padding-right: 10px;
+}
+
+#messagetoolbar select
+{
+ font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: #333333;
+}
+
+#messagetoolbar select.mboxlist
+{
+ position: absolute;
+ left: 375px;
+ top: 10px;
+}
+
+#messagetoolbar select.mboxlist option
+{
+ padding-left: 15px;
+}
+
+#messagetoolbar select.mboxlist option[value="0"]
+{
+ padding-left: 2px;
+}
+
+#listcontrols
+{
+ position: absolute;
+ left: 200px;
+ bottom: 20px;
+ height: 16px;
+ width: 500px;
+ font-size: 11px;
+}
+
+#listcontrols a,
+#listcontrols a:active,
+#listcontrols a:visited,
+#mailboxcontrols a,
+#mailboxcontrols a:active,
+#mailboxcontrols a:visited
+{
+ color: #999999;
+ font-size: 11px;
+ text-decoration: none;
+}
+
+#listcontrols a.active,
+#listcontrols a.active:active,
+#listcontrols a.active:visited,
+#mailboxcontrols a.active,
+#mailboxcontrols a.active:active,
+#mailboxcontrols a.active:visited
+{
+ color: #CC0000;
+}
+
+#listcontrols a.active:hover,
+#mailboxcontrols a.active:hover
+{
+ text-decoration: underline;
+}
+
+#messagecountbar
+{
+ position: absolute;
+ bottom: 16px;
+ right: 40px;
+ width: 300px;
+ height: 20px;
+ text-align: right;
+ white-space: nowrap;
+}
+
+#messagecountbar span
+{
+ font-size: 11px;
+ color: #333333;
+}
+
+#messagepartcontainer
+{
+ position: absolute;
+ top: 80px;
+ left: 20px;
+ right: 20px;
+ bottom: 20px;
+}
+
+#mailcontframe
+{
+ position: absolute;
+ top: 85px;
+ left: 200px;
+ right: 40px;
+ bottom: 40px;
+ border: 1px solid #999999;
+ background-color: #F9F9F9;
+ overflow: auto;
+ /* css hack for IE */
+ width: expression((parseInt(document.documentElement.clientWidth)-240)+'px');
+ height: expression((parseInt(document.documentElement.clientHeight)-125)+'px');
+}
+
+
+#messagepartframe
+{
+ border: 1px solid #999999;
+ background-color: #F9F9F9;
+}
+
+
+#partheader
+{
+ position: absolute;
+ top: 10px;
+ left: 220px;
+ height: 40px;
+}
+
+#partheader table td
+{
+ padding-left: 2px;
+ padding-right: 4px;
+ vertical-align: middle;
+ font-size: 11px;
+}
+
+#partheader table td.title
+{
+ color: #666666;
+ font-weight: bold;
+}
+
+#rcmdraglayer
+{
+ width: 300px;
+ border: 1px solid #999999;
+ background-color: #F9F9F9;
+ padding-left: 8px;
+ padding-right: 8px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ font-size: 11px;
+ opacity: 0.6;
+ -moz-opacity: 0.6;
+}
+
+
+/** mailbox list styles */
+
+#mailboxlist-header
+{
+ position: absolute;
+ top: 85px;
+ left: 20px;
+ width: 140px !important;
+/* width: 162px; */
+ height: 13px;
+ padding: 3px 10px 2px 10px;
+ background-color: #EBEBEB;
+ background-image: url(images/listheader_aqua.gif);
+ border: 1px solid #CCCCCC;
+ color: #333333;
+ font-size: 11px;
+ font-weight: bold;
+}
+
+#mailboxlist-container
+{
+ position: absolute;
+ top: 105px;
+ left: 20px;
+ width: 160px;
+ bottom: 40px;
+ border: 1px solid #CCCCCC;
+ background-color: #F9F9F9;
+ overflow: auto;
+ /* css hack for IE */
+ height: expression((parseInt(document.documentElement.clientHeight)-145)+'px');
+}
+
+#mailboxlist
+{
+ width: 100%;
+ height: auto;
+ margin: 0px;
+ padding: 0px;
+ list-style-image: none;
+ list-style-type: none;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+#mailboxlist li
+{
+ /* height: 18px; */
+ font-size: 11px;
+ background: url(images/icons/folder-closed.png) no-repeat;
+ background-position: 10px 1px;
+ border-bottom: 1px solid #EBEBEB;
+/* IE 5.5 margin-left: -16px; */
+}
+
+#mailboxlist li.inbox
+{
+ background-image: url(images/icons/folder-inbox.png);
+}
+
+#mailboxlist li.drafts
+{
+ background-image: url(images/icons/folder-drafts.png);
+}
+
+#mailboxlist li.sent
+{
+ background-image: url(images/icons/folder-sent.png);
+}
+
+#mailboxlist li.junk
+{
+ background-image: url(images/icons/folder-junk.png);
+}
+
+#mailboxlist li.trash
+{
+ background-image: url(images/icons/folder-trash.png);
+}
+
+#mailboxlist li a
+{
+ display: block;
+ padding-left: 32px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ text-decoration: none;
+}
+
+#mailboxlist li, #mailboxlist li.unread
+{
+ /* background-image: url(images/mailbox_list.gif); */
+}
+
+#mailboxlist li.unread
+{
+ font-weight: bold;
+}
+
+#mailboxlist li.selected
+{
+ background-color: #929292;
+ border-bottom: 1px solid #898989;
+}
+
+#mailboxlist li.selected a
+{
+ color: #FFF;
+ font-weight: bold;
+}
+
+#mailboxlist li.droptarget
+{
+ background-color: #FFFFA6;
+}
+
+/* styles for nested folders */
+#mailboxlist ul {
+ list-style: none;
+ padding: 0;
+ margin:0;
+ border-top: 1px solid #EBEBEB;
+}
+
+#mailboxlist ul li {
+ padding-left: 15px;
+ background-position: 25px 1px;
+}
+
+#mailboxlist li.selected li {
+ background-color: #F9F9F9;
+}
+
+#mailboxlist li.unread li {
+ font-weight: normal;
+}
+
+#mailboxlist li.unread li.unread {
+ font-weight: bold;
+}
+
+#mailboxlist li.selected li a{
+ color: black;
+ font-weight: normal;
+}
+
+
+#mailboxcontrols
+{
+ position: absolute;
+ left: 20px;
+ width: 160px;
+ bottom: 20px;
+ height: 16px;
+ overflow: hidden;
+ font-size: 11px;
+}
+
+
+/** message list styles */
+
+body.messagelist
+{
+ margin: 0px;
+ background-color: #F9F9F9;
+}
+
+#messagelist
+{
+ width: 100%;
+ display: table;
+ table-layout: fixed;
+ /* css hack for IE */
+ width: expression(document.getElementById('mailcontframe').clientWidth);
+}
+
+#messagelist thead tr td
+{
+ height: 20px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-left: 2px;
+ padding-right: 4px;
+ vertical-align: middle;
+ border-bottom: 1px solid #999999;
+ color: #333333;
+ background-color: #EBEBEB;
+ background-image: url(images/listheader_aqua.gif);
+ font-size: 11px;
+ font-weight: bold;
+}
+
+#messagelist thead tr td.sortedASC,
+#messagelist thead tr td.sortedDESC
+{
+ background-image: url(images/listheader_dark.gif);
+}
+
+#messagelist thead tr td.sortedASC a
+{
+ background: url(images/sort_asc.gif) top right no-repeat;
+}
+
+#messagelist thead tr td.sortedDESC a
+{
+ background: url(images/sort_desc.gif) top right no-repeat;
+}
+
+#messagelist thead tr td a,
+#messagelist thead tr td a:hover
+{
+ display: block;
+ width: auto !important;
+ width: 100%;
+ color: #333333;
+ text-decoration: none;
+}
+
+#messagelist tbody tr td
+{
+ height: 16px !important;
+ height: 20px;
+ padding: 2px;
+ padding-right: 4px;
+ font-size: 11px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ border-bottom: 1px solid #EBEBEB;
+ cursor: pointer;
+}
+
+#messagelist col
+{
+ display: table-column;
+ text-align: left;
+ vertical-align: middle;
+}
+
+#messagelist tr td.icon
+{
+ width: 16px;
+ vertical-align: middle;
+}
+
+#messagelist tr td.subject
+{
+ overflow: hidden;
+ vertical-align: middle;
+}
+
+#messagelist tr td.size
+{
+ width: 60px;
+ text-align: right;
+ vertical-align: middle;
+}
+
+#messagelist tr td.from,
+#messagelist tr td.to
+{
+ width: 180px;
+ vertical-align: middle;
+}
+
+#messagelist tr td.date
+{
+ width: 110px;
+ vertical-align: middle;
+}
+
+#messagelist tr.message
+{
+ background-color: #FFFFFF;
+}
+
+/*
+#messagelist tr.odd
+{
+ background-color: #F9F9F9;
+}
+*/
+
+#messagelist tr.unread
+{
+ font-weight: bold;
+ background-color: #FFFFFF;
+}
+
+#messagelist tr.selected td
+{
+ font-weight: bold;
+ color: #FFFFFF;
+ background-color: #CC3333;
+}
+
+#messagelist tr.focused td
+{
+ border-bottom: thin dotted;
+ border-top: thin dotted;
+}
+
+#messagelist tr.unfocused td
+{
+ font-weight: bold;
+ color: #FFFFFF;
+ background-color: #929292;
+}
+
+#messagelist tr.selected td a.rcmContactAddress
+{
+ color: #FFFFFF;
+}
+
+#messagelist tr.unfocused td a.rcmContactAddress
+{
+ color: #FFFFFF;
+}
+
+#messagelist tr.deleted td a.rcmContactAddress
+{
+ color: #CCCCCC;
+}
+
+#messagelist tr.deleted td,
+#messagelist tr.deleted td a
+{
+ color: #CCCCCC;
+}
+
+#quicksearchbar
+{
+ position: absolute;
+ top: 60px;
+ right: 40px;
+ width: 182px;
+ height: 20px;
+ text-align: right;
+ background: url('images/searchfield.gif') top left no-repeat;
+}
+
+#quicksearchbar a
+{
+ position: absolute;
+ top: 3px;
+ right: 4px;
+ text-decoration: none;
+}
+
+#quicksearchbar img
+{
+ vertical-align: middle;
+}
+
+#quicksearchbox
+{
+ position: absolute;
+ top: 2px;
+ left: 20px;
+ width: 140px;
+ font-size: 11px;
+ padding: 0px;
+ border: none;
+}
+
+
+/*\*/
+html>body*#quicksearchbar { background-image: none; }
+html>body*#quicksearchbar a { top: 5px; }
+html>body*#quicksearchbar #quicksearchbox { width: 180px; top:0px; right: 1px; left: auto; }
+/**/
+
+
+#rcversion
+{
+ position: absolute;
+ top: 67px;
+ left: 20px;
+ width: 160px;
+ text-align: center;
+
+ font-weight: normal;
+ font-size: x-small;
+ font-variant: small-caps;
+
+ color: #999999;
+ /*border: 1px solid #308014;
+ background-color: #b4eeb4;*/
+}
+
+/** message view styles */
+
+
+#messageframe
+{
+ position: absolute;
+ top: 85px;
+ left: 200px;
+ right: 40px;
+ bottom: 40px;
+ border: 1px solid #cccccc;
+ background-color: #FFFFFF;
+ overflow: auto;
+ /* css hack for IE */
+ /* margin-bottom: 10px; */
+ width: expression((parseInt(document.documentElement.clientWidth)-240)+'px');
+ height: expression((parseInt(document.documentElement.clientHeight)-125)+'px');
+}
+
+table.headers-table
+{
+ width: 100%;
+ background-color: #EBEBEB;
+ table-layout: fixed;
+
+}
+
+table.headers-table tr td
+{
+ font-size: 11px;
+ border-bottom:1px solid #FFFFFF;
+}
+
+table.headers-table td.header-title
+{
+ width: 80px;
+ color: #666666;
+ font-weight: bold;
+ text-align: right;
+ white-space: nowrap;
+ padding-right: 4px;
+}
+
+table.headers-table tr td.subject
+{
+ width: 95%;
+ font-weight: bold;
+}
+
+#attachment-list
+{
+ margin: 0px;
+ padding: 0px 0px 0px 68px;
+ height: 18px;
+ list-style-image: none;
+ list-style-type: none;
+ background: url(images/icons/attachment.png) 52px 1px no-repeat #DFDFDF;
+ border-bottom: 1px solid #FFFFFF;
+}
+
+#attachment-list li
+{
+/* display: block; */
+ float: left;
+ height: 18px;
+ font-size: 11px;
+ padding: 2px 10px 0px 10px;
+}
+
+#attachment-list li a
+{
+ text-decoration: none;
+}
+
+#attachment-list li a:hover
+{
+ text-decoration: underline;
+}
+
+#messagebody
+{
+ min-height: 300px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ background-color: #FFFFFF;
+}
+
+div.message-part
+{
+ padding: 8px;
+ padding-top: 10px;
+ overflow: hidden;
+}
+
+div.message-part a
+{
+ color: #0000CC;
+}
+
+div.message-part pre
+{
+ margin: 0px;
+ padding: 0px;
+}
+
+
+#remote-objects-message
+{
+ display: none;
+ height: 20px;
+ min-height: 20px;
+ margin: 8px 8px 0px 8px;
+ padding: 10px 10px 6px 46px;
+}
+
+#remote-objects-message a
+{
+ color: #666666;
+ padding-left: 10px;
+}
+
+#remote-objects-message a:hover
+{
+ color: #333333;
+}
+
+
+/** message compose styles */
+
+#priority-selector
+{
+ position: absolute;
+ left: 280px;
+ top: 10px;
+}
+
+#receipt-selector
+{
+ position: absolute;
+ left: 450px;
+ top: 10px;
+}
+
+#compose-container
+{
+ position: absolute;
+ top: 90px;
+ left: 200px;
+ right: 40px;
+ bottom: 40px;
+ padding: 0px;
+ margin: 0px;
+ /* css hack for IE */
+ width: expression(document.documentElement.clientWidth-240);
+ height: expression((parseInt(document.documentElement.clientHeight)-130)+'px');
+}
+
+/*
+#compose-headers
+{
+ position: absolute;
+ top: 70px;
+ left: 200px;
+ height: 84px;
+ border-top: 1px solid #cccccc;
+ overflow: auto;
+}
+
+#compose-headers td
+{
+ padding-top: 1px;
+ padding-bottom: 1px;
+ border-right: 1px solid #cccccc;
+ border-bottom: 1px solid #cccccc;
+}
+*/
+
+#compose-headers
+{
+ width: 100%;
+}
+
+/*
+#compose-headers td
+{
+ width: 100%;
+}
+*/
+
+#compose-headers td.top
+{
+ vertical-align: top;
+}
+
+#compose-headers td.title,
+#compose-subject td.title
+{
+ width: 80px !important;
+ color: #666666;
+ font-size: 11px;
+ font-weight: bold;
+ padding-right: 10px;
+ white-space: nowrap;
+}
+
+#compose-headers td.add-button
+{
+ width: 40px !important;
+ text-align: right;
+ vertical-align: bottom;
+}
+
+#compose-headers td.add-button a
+{
+ color: #666666;
+ font-size: 11px;
+ text-decoration: none;
+}
+
+#compose-headers td textarea
+{
+ width: 100%;
+ height: 40px;
+}
+
+#compose-headers td input
+{
+ width: 100%;
+}
+
+#compose-cc,
+#compose-bcc,
+#compose-replyto
+{
+ display: none;
+}
+
+#compose-body
+{
+ margin-top: 5px;
+ margin-bottom: 10px;
+ width: 99%;
+ height: 90%;
+ min-height: 280px;
+ font-size: 9pt;
+ font-family: "Courier New", Courier, monospace;
+}
+
+#compose-attachments
+{
+ position: absolute;
+ top: 100px;
+ left: 20px;
+ width: 160px;
+}
+
+#compose-attachments ul
+{
+ margin: 0px;
+ padding: 0px;
+ border: 1px solid #CCCCCC;
+ background-color: #F9F9F9;
+ list-style-image: none;
+ list-style-type: none;
+}
+
+#compose-attachments ul li
+{
+ height: 18px;
+ font-size: 11px;
+ padding-left: 2px;
+ padding-top: 2px;
+ padding-right: 4px;
+ border-bottom: 1px solid #EBEBEB;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+#attachment-title
+{
+ background: url(images/icons/attachment.png) top left no-repeat;
+ padding: 0px 0px 3px 22px;
+}
+
+#attachment-form
+{
+ position: absolute;
+ top: 150px;
+ left: 20px;
+ z-index: 200;
+ padding: 8px;
+ visibility: hidden;
+ border: 1px solid #CCCCCC;
+ background-color: #F9F9F9;
+}
+
+#attachment-form input.button
+{
+ margin-top: 8px;
+}
+
+#rcmquotadisplay
+{
+ color: #999999;
+ font-size: 11px;
+}
--- /dev/null
+<public:component>
+<public:attach event="onpropertychange" onevent="propertyChanged()" />
+<script>
+
+var supported = /MSIE (5\.5)|[6789]/.test(navigator.userAgent) && navigator.platform == "Win32";
+var realSrc;
+var blankSrc = "skins/default/images/blank.gif";
+
+if (supported) fixImage();
+
+function propertyChanged() {
+ if (!supported) return;
+
+ var pName = event.propertyName;
+ if (pName != "src") return;
+ // if not set to blank
+ if ( ! new RegExp(blankSrc).test(src))
+ fixImage();
+};
+
+function fixImage() {
+ // get src
+ var src = element.src;
+
+ // check for real change
+ if (src == realSrc) {
+ element.src = blankSrc;
+ return;
+ }
+
+ if ( ! new RegExp(blankSrc).test(src)) {
+ // backup old src
+ realSrc = src;
+ element._original_src = realSrc;
+ }
+
+ // test for png
+ if ( /\.png$/.test( realSrc.toLowerCase() ) ) {
+ // set blank image
+ element.src = blankSrc;
+ // set filter
+ element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
+ src + "',sizingMethod='crop')";
+ }
+ else {
+ // remove filter
+ element.runtimeStyle.filter = "";
+ }
+}
+
+</script>
+</public:component>
\ No newline at end of file
--- /dev/null
+/***** RoundCube|Mail message print styles *****/
+
+body
+{
+ background-color: #ffffff;
+ color: #000000;
+ margin: 2mm;
+}
+
+body, td, th, span, div, p, h3
+{
+ font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+ font-size: 9pt;
+ color: #000000;
+}
+
+h3
+{
+ font-size: 18px;
+ color: #000000;
+}
+
+a, a:active, a:visited
+{
+ color: #000000;
+}
+
+#header
+{
+ margin-left: 5mm;
+ margin-bottom: 3mm;
+}
+
+#messageframe
+{
+ position: relative;
+}
+
+table.headers-table
+{
+ table-layout: fixed;
+}
+
+table.headers-table tr td
+{
+ font-size: 9pt;
+}
+
+table.headers-table td.header-title
+{
+ color: #666666;
+ font-weight: bold;
+ text-align: right;
+ vertical-align: top;
+ padding-right: 4mm;
+ white-space: nowrap;
+}
+
+table.headers-table tr td.subject
+{
+ width: 90%;
+ font-weight: bold;
+}
+
+#attachment-list
+{
+ margin-top: 3mm;
+ padding-top: 3mm;
+ border-top: 1pt solid #cccccc;
+}
+
+#attachment-list li
+{
+ font-size: 9pt;
+}
+
+#attachment-list li a
+{
+ text-decoration: none;
+}
+
+#attachment-list li a:hover
+{
+ text-decoration: underline;
+}
+
+#messagebody
+{
+ margin-top: 5mm;
+ border-top: none;
+}
+
+div.message-part
+{
+ padding: 2mm;
+ margin-top: 5mm;
+ margin-bottom: 5mm;
+ border-top: 1pt solid #cccccc;
+}
+
+div.message-part a
+{
+ color: #0000CC;
+}
+
+div.message-part pre
+{
+ margin: 0;
+ padding: 0;
+ font-size: 9pt;
+}
--- /dev/null
+/***** RoundCube|Mail settings task styles *****/
+
+
+#tabsbar
+{
+ position: absolute;
+ top: 50px;
+ left: 220px;
+ right: 60px;
+ height: 22px;
+ border-bottom: 1px solid #999999;
+ white-space: nowrap;
+ /* css hack for IE */
+ width: expression((parseInt(document.documentElement.clientWidth)-280)+'px');
+}
+
+span.tablink,
+span.tablink-selected
+{
+ float: left;
+ width: 100px;
+ height: 24px !important;
+ height: 22px;
+ background: url('images/tab_pas.gif') top left no-repeat;
+}
+
+span.tablink-selected
+{
+ background: url('images/tab_act.gif') top left no-repeat;
+}
+
+span.tablink a,
+span.tablink-selected a
+{
+ display: block;
+ padding-left: 10px;
+ padding-top: 5px;
+ color: #555555;
+ text-decoration: none;
+}
+
+span.tablink-selected a
+{
+ color: #000000;
+}
+
+#userprefs-box
+{
+ position: absolute;
+ top: 95px;
+ left: 20px;
+ width: 550px;
+ border: 1px solid #999999;
+}
+
+#userprefs-box table td.title
+{
+ color: #666666;
+ padding-right: 10px;
+}
+
+#identities-list,
+#folder-manager
+{
+ position: absolute;
+ top: 95px;
+ left: 20px;
+}
+
+#folder-manager
+{
+ width: 500px;
+ bottom: 120px;
+ overflow: auto;
+ border: 1px solid #999999;
+ height: expression((parseInt(document.documentElement.clientHeight)-215)+'px');
+}
+
+#identities-table
+{
+ width: 500px;
+ border: 1px solid #999999;
+ background-color: #F9F9F9;
+}
+
+#identities-table tbody td
+{
+ cursor: pointer;
+}
+
+#identity-frame
+{
+ position: relative;
+ margin-top: 20px;
+ border: 1px solid #999999;
+}
+
+#identity-details
+{
+ margin-top: 30px;
+ width: 500px;
+ border: 1px solid #999999;
+}
+
+#identity-details table td.title
+{
+ color: #666666;
+ font-weight: bold;
+ text-align: right;
+ padding-right: 10px;
+}
+
+#bottomboxes
+{
+ position: absolute;
+ width: 500px;
+ height: 100px;
+ left: 20px;
+ bottom: 20px;
+}
+
+#userprefs-title,
+#identity-title,
+div.boxtitle,
+#subscription-table thead td
+{
+ height: 12px !important;
+ padding: 4px 20px 3px 6px;
+ border-bottom: 1px solid #999999;
+ color: #333333;
+ font-size: 11px;
+ font-weight: bold;
+ background-color: #EBEBEB;
+ background-image: url(images/listheader_aqua.gif);
+}
+
+div.settingsbox
+{
+ width: 500px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 1px solid #999999;
+}
+
+div.settingspart
+{
+ display: block;
+ padding: 10px;
+}
+
+#subscription-table
+{
+ width: 100%;
+ table-layout: fixed;
+}
+
+#subscription-table tbody td
+{
+ padding-left: 6px;
+ padding-right: 20px;
+ white-space: nowrap;
+ border-bottom: 1px solid #EBEBEB;
+ background-color: #F9F9F9;
+}
+
+#subscription-table td.name
+{
+ width: 280px;
+}
+
+#subscription-table td.subscribed
+{
+ width: 80px;
+}
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/addresses.css" />
+</head>
+<body class="iframe">
+
+<div id="contact-title"><roundcube:label name="addcontact" /></div>
+
+<div id="contact-details">
+<roundcube:object name="contacteditform" size="40" />
+
+<p><br />
+<input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" />
+<roundcube:button command="save" type="input" class="button" label="save" />
+</p>
+
+</div>
+
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:include file="/includes/settingstabs.html" />
+
+
+<div id="identities-list">
+<roundcube:object name="itentitiesList" id="identities-table" class="records-table" cellspacing="0" summary="Identities list" editIcon="" />
+
+<p><roundcube:button command="add" type="input" label="newidentity" class="button" /></p>
+
+<div id="identity-details">
+<div id="identity-title"><roundcube:label name="newidentity" /></div>
+
+<div style="padding:15px;">
+<roundcube:object name="identityform" size="40" />
+
+<p><br />
+<roundcube:button command="save" type="input" class="button" label="save" />
+</p>
+</div>
+</div>
+</div>
+
+<roundcube:include file="/includes/settingscripts.html" />
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/addresses.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+
+<div id="abooktoolbar">
+<roundcube:button command="add" imageSel="/images/buttons/add_contact_sel.png" imageAct="/images/buttons/add_contact_act.png" imagePas="/images/buttons/add_contact_pas.png" width="32" height="32" title="newcontact" />
+<roundcube:button command="delete" imageSel="/images/buttons/delete_sel.png" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletecontact" />
+<roundcube:button command="compose" imageSel="/images/buttons/compose_sel.png" imageAct="/images/buttons/compose_act.png" imagePas="/images/buttons/compose_pas.png" width="32" height="32" title="composeto" />
+<roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="print" />
+<roundcube:button command="export" imageSel="/images/buttons/download_sel.png" imageAct="/images/buttons/download_act.png" imagePas="/images/buttons/download_pas.png" width="32" height="32" title="export" />
+<roundcube:button command="ldappublicsearch" imageSel="/images/buttons/contacts_sel.png" imageAct="/images/buttons/contacts_act.png" imagePas="/images/buttons/contacts_pas.png" width="32" height="32" title="ldapsearch" />
+</div>
+
+<div id="abookcountbar">
+<roundcube:button command="previouspage" imageSel="/images/buttons/previous_sel.png" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previouspage" />
+ <roundcube:object name="recordsCountDisplay" />
+<roundcube:button command="nextpage" imageSel="/images/buttons/next_sel.png" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextpage" />
+</div>
+
+<div id="addresslist">
+<roundcube:object name="addresslist" id="contacts-table" class="records-table" cellspacing="0" summary="Contacts list" />
+</div>
+
+<div id="contacts-box">
+<roundcube:object name="addressframe" id="contact-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" />
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="productname" /> :: <roundcube:label name="compose" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/mail.css" />
+<link rel="stylesheet" type="text/css" href="/googiespell.css" />
+<script type="text/javascript">
+<!--
+
+function rcmail_toggle_display(id)
+ {
+ var row, disp;
+ if (row = document.getElementById(id))
+ {
+ disp = (!row.style.display || row.style.display=='none') ? ((document.all && !window.opera) ? 'block' : 'table-row') : 'none';
+ row.style.display = disp;
+ }
+
+ return false;
+ }
+
+//-->
+</script>
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+
+<form name="form" action="./" method="post">
+
+<div id="messagetoolbar">
+<!--<roundcube:button command="list" image="/images/buttons/back_act.png" imageSel="/images/buttons/back_sel.png" imageAct="/images/buttons/back_act.png" width="32" height="32" title="backtolist" />-->
+<roundcube:button command="send" imageSel="/images/buttons/send_sel.png" imageAct="/images/buttons/send_act.png" imagePas="/images/buttons/send_pas.png" width="32" height="32" title="sendmessage" />
+<roundcube:button command="spellcheck" imageSel="/images/buttons/spellcheck_sel.png" imageAct="/images/buttons/spellcheck_act.png" imagePas="/images/buttons/spellcheck_pas.png" width="32" height="32" title="checkspelling" />
+<roundcube:button command="add-attachment" imageSel="/images/buttons/attach_sel.png" imageAct="/images/buttons/attach_act.png" imagePas="/images/buttons/attach_pas.png" width="32" height="32" title="addattachment" />
+<roundcube:button command="savedraft" imageSel="/images/buttons/drafts_sel.png" imageAct="/images/buttons/drafts_act.png" imagePas="/images/buttons/drafts_pas.png" width="32" height="32" title="savemessage" />
+
+<div id="priority-selector">
+<label for="rcmcomposepriority"><roundcube:label name="priority" />:</label> <roundcube:object name="prioritySelector" form="form" id="rcmcomposepriority" />
+</div>
+
+<div id="receipt-selector">
+<roundcube:object name="receiptCheckBox" form="form" id="rcmcomposereceipt" /> <label for="rcmcomposereceipt"><roundcube:label name="returnreceipt" /></label>
+</div>
+
+</div>
+
+<div id="compose-container">
+<table border="0" cellspacing="0" cellpadding="1" style="width:100%; height:99%;" summary="">
+<tbody>
+<tr>
+<td>
+
+<table border="0" cellspacing="0" cellpadding="1" id="compose-headers" summary="">
+<tbody><tr>
+
+<td class="title"><label for="rcmcomposefrom"><roundcube:label name="from" /></label></td>
+<td><roundcube:object name="composeHeaders" part="from" form="form" id="rcmcomposefrom" tabindex="1" /></td>
+
+</tr><tr>
+
+<td class="title top"><label for="rcmcomposeto"><roundcube:label name="to" /></label></td>
+<td><roundcube:object name="composeHeaders" part="to" form="form" id="rcmcomposeto" cols="80" rows="2" tabindex="2" /></td>
+<td class="add-button"><a href="#" onclick="return rcmail_toggle_display('compose-cc')">[Cc]</a><br />
+<a href="#" onclick="return rcmail_toggle_display('compose-bcc')">[Bcc]</a><br /></td>
+
+</tr><tr id="compose-cc">
+
+<td class="title top"><label for="rcmcomposecc"><roundcube:label name="cc" /></label></td>
+<td><roundcube:object name="composeHeaders" part="cc" form="form" id="rcmcomposecc" cols="80" rows="2" tabindex="3" /></td>
+
+</tr><tr id="compose-bcc">
+
+<td class="title top"><label for="rcmcomposebcc"><roundcube:label name="bcc" /></label></td>
+<td><roundcube:object name="composeHeaders" part="bcc" form="form" id="rcmcomposebcc" cols="80" rows="2" tabindex="4" /></td>
+
+</tr><tr id="compose-replyto">
+
+<td class="title top"><label for="rcmcomposereplyto"><roundcube:label name="replyto" /></label></td>
+<td><roundcube:object name="composeHeaders" part="replyto" form="form" id="rcmcomposereplyto" size="80" tabindex="5" /></td>
+
+</tr><tr>
+
+<td class="title"><label for="compose-subject"><roundcube:label name="subject" /></label></td>
+<td><roundcube:object name="composeSubject" id="compose-subject" form="form" tabindex="6" /></td>
+
+</tr>
+</tbody>
+</table>
+
+</td>
+
+</tr><tr>
+
+<td style="width:100%; height:98%; vertical-align:top;">
+<roundcube:object name="composeBody" id="compose-body" form="form" cols="80" rows="20" wrap="virtual" tabindex="7" />
+
+<table border="0" cellspacing="0" width="100%" summary=""><tbody><tr>
+
+<td>
+<roundcube:button type="input" command="send" class="button" label="sendmessage" />
+<roundcube:button type="input" command="list" class="button" label="cancel" />
+</td>
+<td align="right">
+<roundcube:label name="charset" />: <roundcube:object name="charsetSelector" tabindex="8" />
+</td>
+
+</tr></tbody></table>
+
+</td>
+
+</tr>
+</tbody>
+</table>
+
+</div>
+
+<div id="compose-attachments">
+<div id="attachment-title"><roundcube:label name="attachments" /></div>
+<roundcube:object name="composeAttachmentList" deleteIcon="/images/icons/remove-attachment.png"/ >
+<p><roundcube:button command="add-attachment" imagePas="/images/buttons/add_pas.png" imageSel="/images/buttons/add_sel.png" imageAct="/images/buttons/add_act.png" width="23" height="18" title="addattachment" />
+</div>
+
+</form>
+
+<roundcube:object name="composeAttachmentForm" id="attachment-form" />
+
+<script type="text/javascript">
+<!--
+
+var cc_field = document.form._cc;
+if (cc_field && cc_field.value!='')
+ rcmail_toggle_display('compose-cc');
+
+//-->
+</script>
+
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/addresses.css" />
+</head>
+<body class="iframe">
+
+<div id="contact-title"><roundcube:label name="editcontact" /></div>
+
+<div id="contact-details">
+<roundcube:object name="contacteditform" size="40" />
+
+<p><br />
+<roundcube:button command="show" type="input" class="button" label="cancel" />
+<roundcube:button command="save" type="input" class="button" label="save" />
+</p>
+
+</form>
+</div>
+
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:include file="/includes/settingstabs.html" />
+
+
+<div id="identities-list">
+<roundcube:object name="itentitiesList" id="identities-table" class="records-table" cellspacing="0" summary="Identities list" editIcon="" />
+
+<p><roundcube:button command="add" type="input" label="newidentity" class="button" /></p>
+
+<div id="identity-details">
+<div id="identity-title"><roundcube:label name="edititem" /></div>
+
+<div style="padding:15px;">
+<roundcube:object name="identityform" size="40" />
+
+<p><br />
+<roundcube:button command="delete" type="input" class="button" label="delete" />
+<roundcube:button command="save" type="input" class="button" label="save" />
+</p>
+</div>
+</div>
+</div>
+
+<roundcube:include file="/includes/settingscripts.html" />
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RoundCube|Mail :: ERROR</title>
+<roundcube:include file="/includes/links.html" />
+</head>
+<body>
+
+<div id="header"><img src="/images/roundcube_logo.png" width="165" height="55" alt="RoundCube Webmail" /></div>
+
+<div style="width:400px; margin:60px auto;">
+$__page_content
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:include file="/includes/settingstabs.html" />
+
+<div id="identities-list">
+<roundcube:object name="itentitiesList" id="identities-table" class="records-table" cellspacing="0" summary="Identities list" editIcon="" />
+
+<p><roundcube:button command="add" type="input" label="newidentity" class="button" /></p>
+</div>
+
+<roundcube:include file="/includes/settingscripts.html" />
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<link rel="stylesheet" type="text/css" href="/common.css" />
+<link rel="stylesheet" type="text/css" href="/ldapsearchform.css" />
+</head>
+<body class="iframe">
+
+<div id="ldapsearch-title"><roundcube:label name="ldappublicsearchform" /></div>
+
+<div id="ldapsearch-details">
+<roundcube:object name="ldappublicsearch" size="40" />
+<p>
+<roundcube:button command="ldappublicsearch" type="input" class="button" label="ldappublicsearch" />
+<input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" />
+<br /></p>
+</div>
+
+
+<div id="ldapsearch-results">
+<roundcube:object name="ldappublicaddresslist"
+ id="ldappublicaddresslist"
+ cellspacing="0"
+ summary="Ldap email address list" />
+</div>
+
+<roundcube:include file="/includes/ldapscripts.html" />
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<style type="text/css">
+
+#login-form {
+ margin-left: auto;
+ margin-right: auto;
+ margin-top: 50px;
+ width: 350px;
+}
+
+</style>
+</head>
+<body>
+
+<img src="skins/default/images/roundcube_logo.png" id="rcmbtn104" width="165" height="55" border="0" alt="RoundCube Webmail" hspace="10" />
+
+<roundcube:object name="message" id="message" />
+
+<div id="login-form">
+<form name="form" action="./" method="post">
+<roundcube:object name="loginform" form="form" />
+
+<p style="text-align: center;"><input type="submit" class="button" value="<roundcube:label name="login" />" />
+
+</form>
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/mail.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+
+<div id="messagetoolbar">
+<roundcube:button command="checkmail" imageSel="/images/buttons/inbox_sel.png" imageAct="/images/buttons/inbox_act.png" imagePas="/images/buttons/inbox_pas.png" width="32" height="32" title="checkmail" />
+<roundcube:button command="compose" imageSel="/images/buttons/compose_sel.png" imageAct="/images/buttons/compose_act.png" imagePas="/images/buttons/compose_pas.png" width="32" height="32" title="writenewmessage" />
+<roundcube:button command="reply" imageSel="/images/buttons/reply_sel.png" imageAct="/images/buttons/reply_act.png" imagePas="/images/buttons/reply_pas.png" width="32" height="32" title="replytomessage" />
+<roundcube:button command="reply-all" imageSel="/images/buttons/replyall_sel.png" imageAct="/images/buttons/replyall_act.png" imagePas="/images/buttons/replyall_pas.png" width="32" height="32" title="replytoallmessage" />
+<roundcube:button command="forward" imageSel="/images/buttons/forward_sel.png" imageAct="/images/buttons/forward_act.png" imagePas="/images/buttons/forward_pas.png" width="32" height="32" title="forwardmessage" />
+<roundcube:button command="delete" imageSel="/images/buttons/delete_sel.png" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletemessage" />
+<roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" />
+</div>
+
+<div id="quicksearchbar">
+<roundcube:object name="searchform" type="search" results="5" id="quicksearchbox" /><roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" />
+</div>
+
+<div id="messagecountbar">
+<roundcube:button command="previouspage" imageSel="/images/buttons/previous_sel.png" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previousmessages" />
+ <roundcube:object name="messageCountDisplay" />
+<roundcube:button command="nextpage" imageSel="/images/buttons/next_sel.png" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextmessages" />
+</div>
+
+<div id="mailboxlist-header"><roundcube:label name="mailboxlist" /></div>
+<div id="mailboxlist-container"><roundcube:object name="mailboxlist" id="mailboxlist" maxlength="16" /></div>
+
+<div id="mailboxcontrols">
+<roundcube:label name="folder" />:
+<roundcube:button command="expunge" label="compact" classAct="active" />
+<roundcube:button command="purge" label="empty" classAct="active" />
+</div>
+
+
+<div id="mailcontframe">
+<roundcube:object name="messages"
+ id="messagelist"
+ cellspacing="0"
+ summary="Message list"
+ messageIcon="/images/icons/dot.png"
+ unreadIcon="/images/icons/unread.png"
+ deletedIcon="/images/icons/deleted.png"
+ repliedIcon="/images/icons/replied.png"
+ attachmentIcon="/images/icons/attachment.png" />
+</div>
+
+<div id="listcontrols">
+<roundcube:label name="select" />:
+<roundcube:button command="select-all" label="all" classAct="active" />
+<roundcube:button command="select-all" prop="unread" label="unread" classAct="active" />
+<roundcube:button command="select-none" label="none" classAct="active" />
+<roundcube:label name="quota" />: <roundcube:object name="quotaDisplay" />
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:include file="/includes/settingstabs.html" />
+
+<form name="subscriptionform" action="./" onsubmit="rcmail.command('create-folder');return false;">
+
+<div id="folder-manager">
+<roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table"
+ cellpadding="1" cellspacing="0" summary="Folder subscription table"
+ deleteIcon="/images/icons/folder-trash.png"
+ renameIcon="/images/icons/edit.png" />
+</div>
+
+<div id="bottomboxes">
+<div class="settingsbox">
+<div class="boxtitle"><roundcube:label name="createfolder" /></div>
+
+<div class="settingspart">
+<roundcube:label name="foldername" />:
+<roundcube:object name="createfolder" form="subscriptionform" />
+<roundcube:button command="create-folder" type="input" class="button" label="create" />
+</div>
+</div>
+
+</div>
+
+</form>
+
+<roundcube:include file="/includes/settingscripts.html" />
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/mail.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+
+<div id="messagecountbar">
+<roundcube:button command="previousmessage" imageSel="/images/buttons/previous_sel.png" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previousmessages" />
+ <roundcube:object name="messageCountDisplay" />
+<roundcube:button command="nextmessage" imageSel="/images/buttons/next_sel.png" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextmessages" />
+</div>
+
+<div id="messagetoolbar">
+<roundcube:button command="list" image="/images/buttons/back_act.png" imageSel="/images/buttons/back_sel.png" imageAct="/images/buttons/back_act.png" width="32" height="32" title="backtolist" />
+<roundcube:button command="compose" imageSel="/images/buttons/compose_sel.png" imageAct="/images/buttons/compose_act.png" imagePas="/images/buttons/compose_pas.png" width="32" height="32" title="writenewmessage" />
+<roundcube:button command="reply" imageSel="/images/buttons/reply_sel.png" imageAct="/images/buttons/reply_act.png" imagePas="/images/buttons/reply_pas.png" width="32" height="32" title="replytomessage" />
+<roundcube:button command="reply-all" imageSel="/images/buttons/replyall_sel.png" imageAct="/images/buttons/replyall_act.png" imagePas="/images/buttons/replyall_pas.png" width="32" height="32" title="replytoallmessage" />
+<roundcube:button command="forward" imageSel="/images/buttons/forward_sel.png" imageAct="/images/buttons/forward_act.png" imagePas="/images/buttons/forward_pas.png" width="32" height="32" title="forwardmessage" />
+<roundcube:button command="delete" imageSel="/images/buttons/delete_sel.png" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletemessage" />
+<roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" />
+<roundcube:button command="viewsource" imageSel="/images/buttons/source_sel.png" imageAct="/images/buttons/source_act.png" imagePas="/images/buttons/source_pas.png" width="32" height="32" title="viewsource" />
+<roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('moveto', this.options[this.selectedIndex].value)" class="mboxlist" />
+</div>
+
+<div id="mailboxlist-header"><roundcube:label name="mailboxlist" /></div>
+<div id="mailboxlist-container"><roundcube:object name="mailboxlist" id="mailboxlist" maxlength="16" /></div>
+
+<div id="messageframe">
+<roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" addicon="/images/icons/plus.gif" summary="Message headers" />
+<roundcube:object name="messageAttachments" id="attachment-list" />
+<roundcube:object name="blockedObjects" id="remote-objects-message" />
+<roundcube:object name="messageBody" id="messagebody" showImages="true" />
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/mail.css" />
+</head>
+<body class="extwin">
+
+<roundcube:include file="/includes/header.html" />
+
+<div id="partheader">
+<roundcube:object name="messagePartControls" cellpadding="2" cellspacing="0" />
+</div>
+
+
+<div id="messagepartcontainer">
+<roundcube:object name="messagePartFrame" id="messagepartframe" width="100%" height="85%" />
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<link rel="stylesheet" type="text/css" href="/print.css" />
+</head>
+<body>
+
+<div id="header"><img src="/images/roundcube_logo_print.gif" width="182" height="50" alt="RoundCube Webmail" /></div>
+
+<div id="messageframe">
+<roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" />
+<roundcube:object name="messageAttachments" id="attachment-list" />
+<roundcube:object name="messageBody" id="messagebody" showImages="false" />
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:include file="/includes/settingstabs.html" />
+
+<div id="userprefs-box">
+<div id="userprefs-title"><roundcube:label name="userpreferences" /></div>
+
+<div style="padding:15px">
+<roundcube:object name="userprefs">
+
+<p><br /><roundcube:button command="save" type="input" class="button" label="save" /></p>
+</div>
+</div>
+
+<roundcube:include file="/includes/settingscripts.html" />
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/addresses.css" />
+</head>
+<body class="iframe">
+
+<div id="contact-title"><roundcube:object name="contactdetails" part="name" /></div>
+
+<div id="contact-details">
+<roundcube:object name="contactdetails" />
+
+<p><br /><roundcube:button command="edit" type="input" class="button" label="editcontact" /></p>
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body style="background-color:#F2F2F2;">
+
+<div style="margin:10px auto; text-align:center">
+<img src="images/rcube_watermark.png" width="245" height="245" alt="" />
+</div>
+
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+Order allow,deny
+Deny from all
\ No newline at end of file